2
for x in $(ls -ll <path to files> | awk '{ print $3,$4 }' | tail -n +2) ; do 
  if [ "${x}" != "root" ] ; then
    echo "Fail"
    break
  else
    echo "Pass"
 fi
done

Now, this prints "Pass" for every file it finds. I want to print "Pass" if all files are owned by root, and print "Fail" if any user or group in list is not root.

0

5 Answers 5

6

If you want to find out if all files in your are owned by root and belong to group root, use find:

find <path to files> ! -user root -or ! -group root -print

If anything is returned, that file either is not owned by root, or does not belong to the group root. You can then put that into a conditional clause to print out Pass or Fail.

[[ "$(find <path to files> ! -user root -or ! -group root -print)" == "" ]] && echo "Pass" || echo "Fail"
1
  • 1
    possibly with a "-maxdepth 1" if recursion isn't desired
    – Grump
    Commented Dec 7, 2018 at 16:50
5

First, you shouldn't parse the output of ls and its variations. You can go about this using stat:

$ stat -c%U-%G ./*
tomasz-tomasz
tomasz-tomasz
tomasz-tomasz

As you can see, the result is a reliable list of two words concatenated, which you can operate on to get the result wanted. Put it into a loop, and there you go:

PASS=true
for i in $(stat -c%U-%G ./*); do
    if ! [[ "$i" == root-root ]]; then
        PASS=false; break
    fi
done
if "$PASS"; then
    echo Pass
else
    echo Fail
fi

The value of i needs to be root-root for the loop to get to its end with the switch unchanged.

Replace ./* with the_dir/* to test another location.

The - separator is needed, because, as Grump noted in the comments, The string comparison may fail if the file is owned by 'roo' and in the group 'troot', so a separator would still be a good thing.

Familiarise yourself with this: Why *not* parse `ls` (and what do to instead)?

9
  • the hyphen - is a poor choice of separator since it is a legal character in usernames on at least some systems; the colon : is a better choice and it's what chown uses too
    – kbolino
    Commented Dec 7, 2018 at 22:57
  • @kbolino But if there's one hyphen in one username, there would be two total in the product of %U-%G, while root-root has only one. So they would not be equal.
    – user147505
    Commented Dec 7, 2018 at 23:00
  • Fair enough, I don't think groups can be named the empty string.
    – kbolino
    Commented Dec 7, 2018 at 23:21
  • Actually, empty group name wouldn't matter at all. The only case where the hyphen would be a problem is where the user name you're looking for has a hyphen as then user john-smith in group users would be indistinguishable from user john in group smith-users. The practical relevance of this exception is pretty low, though.
    – kbolino
    Commented Dec 8, 2018 at 0:00
  • @kbolino Agreed.
    – user147505
    Commented Dec 8, 2018 at 2:03
2

How about

[ 1 = $({ echo root:root; stat -c"%U:%G" *; } | sort -u | wc -l) ] && echo PASS || echo FAIL

EDIT: or

[ -z $(stat -c"%U:%G" * | grep -vm1 root:root) ] && echo PASS || echo FAIL
4
  • Smart, but a short-circuit break after the first fail is a big optimisation.
    – user147505
    Commented Dec 7, 2018 at 22:09
  • Admitted. Added another approach that quits after first non-match.
    – RudiC
    Commented Dec 7, 2018 at 22:23
  • Non-match of what? Is it not the same thing?
    – user147505
    Commented Dec 7, 2018 at 23:02
  • grep's -m1 option makes it leave after the first match (due to the -v: non-match), i.e. the first line not matching "root:root".
    – RudiC
    Commented Dec 8, 2018 at 12:35
0

The way I would do it is probably

found=$(find <path to files> -maxdepth 1 -not \( -user root -group root \) -printf "x")
found=${found:+Fail}
echo ${found:=Pass}

However, the simplest way of altering your script is:

found="Pass"

for x in $(ls -llA <path to files> | awk 'FNR>2{ print $3,$4 }' )
do 
   if [ "${x}" != "root" ]
   then
       found="Fail"
       break
   fi
done

echo $found

here I've added the A flag to catch files that begin with a "."

Doing it this way, however, is not a good idea as filenames containing a newline will cause unexpected results.

0

The output of ls is risky in command lines. I suggest using find for this purpose in the shellscript.

  • The parameters to find -printf are described in man find.
  • The standard output is piped to grep and the exit status is stored in norootfile.
  • The output is also written to a temporary file, and the number of lines is counted and stored in numfile (the number of files found).
  • You can use the option -v 'verbose' to get more details in the output from the shellscript.

If you want to search also hidden files use find . instead of find *

If you don't want to search in subdirectories, use -maxdepth 1 in the find command line.

#!/bin/bash

if [ "$1" == "-h" ]
then
 echo "Usage: $0 -h  # this help text"
 echo "       $0 -v  # verbose output"
 exit
fi

tmpfil=$(mktemp)

find * -xtype f -printf "%u:%g  %p\n" | tee "$tmpfil" | grep -v '^root:root' > /dev/null

norootsfile=$?
numfile=$(wc -l "$tmpfil")
#cat "$tmpfil"

if [ ${numfile%% *} -eq 0 ]
then
 echo "No file found; check the current directory"
elif [ $norootsfile -eq 0 ]
then
 echo "Fail"
 if [ "$1" == "-v" ]
 then
  echo "----- Found some file(s) not owned or grouped by root"
  echo "user:group file-name --------------------------------"
  grep -v '^root:root' "$tmpfil"
 fi
else
 echo "Pass"
 if [ "$1" == "-v" ]
 then
  echo "----- Found only files owned or grouped by root"
 fi
fi
rm "$tmpfil"

You must log in to answer this question.

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