Submit Hint Search The Forums LinksStatsPollsHeadlinesRSS
14,000 hints and counting!


Click here to return to the 'Re: Script to list all filesystem objects with ACLs' hint
The following comments are owned by whoever posted them. This site is not responsible for what they say.
Re: Script to list all filesystem objects with ACLs
Authored by: mzso on Aug 20, '08 03:26:43AM

The various control characters + newlines in the script make it quite sensitive (easily broken) and a bit difficult to reproduce. I've created an awk variant that pretty much does the same, but it's a lot more resistant to the usual copy&paste errors.

Here's the script (put it into something like "find_acls.sh"):


#!/bin/sh

if [ $(id -u) -ne 0 ]; then
  echo 'Tip: you might want to run this script as the "root" user (eg. via "sudo").'
fi
if [ -n "$1" ]; then
  target=$1
else
  target=/
fi
echo "Started: $(date)"
find -x "${target}" -exec ls -alde '{}' \; 2> /dev/null | awk '{if(match($0,"^ [0-9]+:")>0){if(prevline!=""){print prevline;prevline=""}print $0}else{prevline=$0}}'
echo "Finished: $(date)"

You can execute it as it is or supply a path as parameter if you want to list ACLs only in a directory. On my MacBook Pro it ran for 45-48 minutes. The "find + ls" takes the most of this, the awk script runs only in 7 seconds or so (I mean if you separate the two and do not use a pipe).

Btw. here're two nice primers on ACLS (in case you wonder what an ACL is and how to use, enable or disable it, etc.):



[ Reply to This | # ]
Re: Script to list all filesystem objects with ACLs
Authored by: mzso on Aug 20, '08 04:24:06AM
There's a case where both the original script and my awk variant "fail": if a file or directory name contains a newline and after that a space, a number and a colon. You can create such a file with a command like this:
touch "test file
 0: something.txt"
However I consider this a minor flaw and it'd be quite inefficient if one would try to write a script that considers even this special case. You'd have to create a complete file listing with "find" (using the "-print0" option to have the zero byte as a separator) and query the ACLs one by one for each file and process the output accordingly.

[ Reply to This | # ]
Re: Script to list all filesystem objects with ACLs
Authored by: xr4ti on Aug 22, '08 03:27:49PM

I haven't been a big awk fan over the years, but awk may be a better choice in this case, since it can easily access the previous line.

But this script lacks the databasing features of my original posting. In particular, this script puts each ACL entry on a new line (just like the original ls -ale printout), rather than putting them together as a single record for a spreadsheet (as in the original posting). Since ACL entries can be on multiple lines, the use of prevline in this script would have to be manipulated, and the print would have to avoid inserting newlines until the entire ACL entry was put together.

Also, I prefer to have the output from the 'find' go to a file, and run the awk/sed on the resulting file, rather than running it through a pipe. If something goes wrong before this script ends, you end up with nothing. But if you have the find output in a file, you can see if there is a file with an aberrant name or some other anomaly that caused the awk to fail.

The original posting wasn't intended for the faint of heart in the script world, but it looks like some people have managed to copy it and get it working. I built the original to put the data in a spreadsheet; there is probably a way to do that with awk, but for the time being, I'll settle with the original. Perhaps someone can work on an awk version that produces the same sort of output. (No offense and thanks for the submission!)



[ Reply to This | # ]
Re: Script to list all filesystem objects with ACLs
Authored by: mzso on Aug 24, '08 01:39:06PM

You're right: my previous script did only filter the output of find+ls and did not produce a CSV like structure.

Just for the sake of practise, I've created a pure sed variant that really creates CSV output:


#!/bin/sh

tempfile=
if [ -n "$1" ]; then
  target=$1
else
  target=/
fi

if [ -d "${target}" ]; then
  if [ ! -x "${target}" ]; then
    echo "You have no read access to the specified directory."
    exit 1
  else
    tempfilename=find_acls_csv_$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d" ").dat
    if [ -d "${TMPDIR}" -a -r "${TMPDIR}" -a -w "${TMPDIR}" ]; then
      tempdir=${TMPDIR}
    else
      if [ -d "${TMP}" -a -r "${TMP}" -a -w "${TMP}" ]; then
        tempdir=${TMP}
      else
        if [ -d "/tmp" -a -r "/tmp" -a -w "/tmp" ]; then
          tempdir=/tmp
        else
          curdir=$(pwd)
          if [ -d "${curdir}" -a -r "${curdir}" -a -w "${curdir}" ]; then
            tempdir=${curdir}
          else
            echo 'Could not find a readable and writable temporary directory.'
            exit 5
          fi
        fi
      fi
    fi
    tempfile=${tempdir}/${tempfilename}
    if touch "${tempfile}"; then
      find "${target}" -exec ls -alde '{}' \; > "${tempfile}" 2> /dev/null
      target=${tempfile}
    else
      echo "Could not write to temporary file at ${tempfile}."
      exit 4
    fi
  fi
else
  if [ -f "${target}" ]; then
    if [ ! -r "${target}" ]; then
      echo "You have no read access to the specified file."
      exit 2
    fi
  else
    echo "The specified target path does not exist."
    exit 3
  fi
fi

if [ -n "${tempfile}" -a $(id -u) -ne 0 ]; then
  echo '"Tip: you might want to run this script as the root user (eg. via sudo)."'
fi
echo '"mode","# of links","owner","group","bytes","month","day","time","path","acls"'
cat "${target}" | sed -Ee '
:a
s#^([^/ ]+) +#\1\#*\##
ta
:b
$! {
N
bb
}
s/[[:cntrl:]]*(\n)/\1/g
:c
s#(\n[^/ ]+) +#\1\#*\##g
tc
s#\n ([0-9]+:[^[:cntrl:]]*)#!*!\1#g
s#(\n)([^[:cntrl:]]*!\*!)#\1!\2#g
s#\n[^!][^[:cntrl:]]*##g
s#\n!#\
#g
s#^([^[:cntrl:]]*!\*!)#!\1#g
s#^[^!][^[:cntrl:]]*\n##g
s#^!##g
s#((^|\n)[^![:cntrl:]]+)!\*!#\1\#*\##g
s#"#""#g
s#(.+)#"\1"#
s#\#\*\##","#g
s#\n#"&"#g
s#!\*!#\
#g
'
if [ -n "${tempfile}" ]; then
  rm "${tempfile}"
fi

It seems that the sed in Darwin is not fully following the description of it's manpages. :-( Unfortunately this script turned out to be really slow. It processed my 130MB file listing in several minutes (4-5min).

I've created an Awk script that creates a CSV output as well:


#!/bin/sh

tempfile=
if [ -n "$1" ]; then
  target=$1
else
  target=/
fi

if [ -d "${target}" ]; then
  if [ ! -x "${target}" ]; then
    echo "You have no read access to the specified directory."
    exit 1
  else
    tempfilename=find_acls_csv_$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d" ").dat
    if [ -d "${TMPDIR}" -a -r "${TMPDIR}" -a -w "${TMPDIR}" ]; then
      tempdir=${TMPDIR}
    else
      if [ -d "${TMP}" -a -r "${TMP}" -a -w "${TMP}" ]; then
        tempdir=${TMP}
      else
        if [ -d "/tmp" -a -r "/tmp" -a -w "/tmp" ]; then
          tempdir=/tmp
        else
          curdir=$(pwd)
          if [ -d "${curdir}" -a -r "${curdir}" -a -w "${curdir}" ]; then
            tempdir=${curdir}
          else
            echo 'Could not find a readable and writable temporary directory.'
            exit 5
          fi
        fi
      fi
    fi
    tempfile=${tempdir}/${tempfilename}
    if touch "${tempfile}"; then
      find "${target}" -exec ls -alde '{}' \; > "${tempfile}" 2> /dev/null
      target=${tempfile}
    else
      echo "Could not write to temporary file at ${tempfile}."
      exit 4
    fi
  fi
else
  if [ -f "${target}" ]; then
    if [ ! -r "${target}" ]; then
      echo "You have no read access to the specified file."
      exit 2
    fi
  else
    echo "The specified target path does not exist."
    exit 3
  fi
fi

if [ -n "${tempfile}" -a $(id -u) -ne 0 ]; then 
  echo '"Tip: you might want to run this script as the root user (eg. via sudo)."'
fi
echo '"mode","# of links","owner","group","bytes","month","day","time","path","acls"'
awk '
{
  if (match($0, "^ [0-9]+:") > 0) {
    acls++
    gsub("^ +", "", $0);
    if (acls > 1) {
      buffer = buffer "\n" $0
    }
    else {
      buffer = buffer ",\"" $0
    }
  }
  else {
    if (buffer != "" && acls > 0) {
      print buffer "\""
    }
    acls = 0
    buffer = ""
    for (i = 1; i <= 8; i++) {
      gsub("\"", "\"\"", $i)
      buffer = buffer "\"" $i "\","
    }
    path = substr($0, index($0, $8) + 5)
    sub("^ +", "", path)
    gsub("\"", "\"\"", path)
    buffer = buffer "\"" path "\""
  }
}' "${target}"

if [ -n "${tempfile}" ]; then
  rm "${tempfile}"
fi
This one processed the 130MB file listing in 20 seconds. The output is a comma separated file where multiple ACLs are kept in a single field (spreadsheet cell).

[ Reply to This | # ]