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: 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 | # ]