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.

K7AAY
  • 3,816
  • 4
  • 23
  • 39

5 Answers5

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"
Rui F Ribeiro
  • 56,709
  • 26
  • 150
  • 232
Lewis M
  • 1,048
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)?

  • 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 Dec 07 '18 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. –  Dec 07 '18 at 23:00
  • Fair enough, I don't think groups can be named the empty string. – kbolino Dec 07 '18 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 Dec 08 '18 at 00:00
  • @kbolino Agreed. –  Dec 08 '18 at 02:03
  • Thank you all so much! I think this stat solution is what I am looking for. Does anyone see an issue at adding "2> /dev/null" to the end of the line? The unfortunate part of my task is that this is part of larger script to check for compliance and there are cases where this set of files must be checked for root ownership, but they might not exist on all hosts. My next approach would be to add a conditional statement in there for if file exists – Caleb Hoch Dec 10 '18 at 13:53
  • @CalebHoch At the end of which line? If you want to get rid of the errors for stat, then place it inside the substitution, ie. $(stat -c%U-%G ./* 2>/dev/null). But better first check if there's anything to stat or adjust your script's logic. Also be careful with hidden files like .sth, as ./* doesn't catch them. You need ./.* for them. –  Dec 10 '18 at 17:07
  • @Tomasz I found where the error was. When i was testing the command by itself to see the output, I Used sudo. I accidentally copied that into my script with sudo so it was printing a "Last login" line. Initially I just used grep-v "Last Login" because I thought it might be some system specific reason it was doing that, but then i removed sudo from the script and it began working. Thanks for the help! This is perfect. Im definitely going to read that link you posted too. – Caleb Hoch Dec 10 '18 at 19:48
  • @CalebHoch Good. Whatever you end up with, remember to test against an empty directory to check your logic. –  Dec 10 '18 at 20:36
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
RudiC
  • 8,969
  • Smart, but a short-circuit break after the first fail is a big optimisation. –  Dec 07 '18 at 22:09
  • Admitted. Added another approach that quits after first non-match. – RudiC Dec 07 '18 at 22:23
  • Non-match of what? Is it not the same thing? –  Dec 07 '18 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 Dec 08 '18 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.

Grump
  • 225
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"
sudodus
  • 6,421