3

I think I'm close here but I'm missing something stupid. I'm trying to make awk print out the filenames for files that are missing extended attributes or are missing an attribute value.

So a file will have something like:

 getfattr -d /path/to/file/testfile.1

 # file: /path/to/file/testfile.1
 user.test="1"

awk should return the filename if two conditions are met; if user.test is missing, or if user.test is null i.e "". It seems like the obvious way to do this is to simply check the NF. If the NF is 2 or less it means that we're missing one or the other and thus we can print the filename. This is what I have so far:

readarray -t PATHS_ARRAY < <(find /files/to/audit type -f)

attr="user.test"

printf -- '%s\0' "${PATHS_ARRAY[@]} |\
xargs -0 getfattr -P --absolute-names --name="$attr" |\
awk -v attr_="${attr}=" '
  BEGIN { FS="[ ,\"]+" }
  $0 ~ ( attr_ ) {
    if ( NF <= 2 ) {
      print fname
      next
    }
  } { fname = $0 }
'

We're separating on ", so for a "correctly" set extended attribute we should get

NF=
1           2 3
user.test=" 2 "

and a broken one will be

NF=
1           2 (or 0 because user.test does not exist)         
user.test=" "
2
  • If I'm reading that right, you have the { fname = $0 } block inside the block with the $0 ~ ( attr_ ) conditional. Shouldn't it be outside?
    – muru
    Commented Aug 4, 2022 at 3:28
  • Yeah, you're right. Just a typo, it's outside in my tests.
    – bcHelix
    Commented Aug 4, 2022 at 6:20

1 Answer 1

4

Use attr instead of getfattr to retrieve the attribute. This utility will, by default, handle attributes in the user namespace. It is easier to pull out only the attribute's value too, so there's no need for parsing:

find /files/to/audit -type f -exec sh -c '
    for pathname do
        attrval=$( attr -q -g test "$pathname" 2>/dev/null )
        if [ -z "$attrval" ]; then
            printf "%s\n" "$pathname"
        fi
    done' sh {} +

Or, shorter,

find /files/to/audit -type f -exec sh -c '
    for pathname do
        [ -z "$( attr -q -g test "$pathname" 2>/dev/null )" ] && printf "%s\n" "$pathname"
    done' sh {} +

These code snippets would call a short in-line script with batches of found regular files from in or under /files/to/audit. The sh -c script loops over the current set of found pathnames to try to get the user.test attribute from each. Any pathname that generates a missing or empty attribute value is printed.

On Debian-based Linux distributions, the getfattr and attr utilities are distributed in the same package (called attr).


Slightly fancier, with a parametrised attribute name and output that will indicate either missing or zero-length attribute values:

attr=test

find /files/to/audit -type f -exec sh -c '
    attr=$1; shift
    for pathname do
        if attrval=$( attr -q -g "$attr" "$pathname" 2>/dev/null )
        then
            if [ -z "$attrval" ]; then
                printf "Empty: %s\n" "$pathname"
            fi
        else
            printf "Missing: %s\n" "$pathname"
        fi
    done' sh "$attr" {} +

Or, following the DRY principle:

attr=test

find /files/to/audit -type f -exec sh -c '
    attr=$1; shift
    for pathname do
        unset -v issue

        if attrval=$( attr -q -g "$attr" "$pathname" 2>/dev/null )
        then
            [ -z "$attrval" ] && issue=Empty
        else
            issue=Missing
        fi

        if [ -n "$issue" ]; then
            printf "%s: %s\n" "$issue" "$pathname"
        fi
    done' sh "$attr" {} +

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .