if statement problem

Writing my script and I'm banging my head on the desk right now ...

My biggest problem is the 3rd IF statement where I check if the username exists. Doing the grep command on it's own in the shell gives me a 1 or 0 value. Running the script, it always returns a false value (runs the ELSE statement) and if I change the IF to != 0 I always get a true value. Help?

#! /bin/bash

if [[ $HOME == /root ]] #check if you are root
then
        if [[ -f /etc/passwd ]] #check if password file exists
        then
                echo "Enter username"
                read username
                        #until [ $username = "done" ]
                        #do
                                if [[ 'grep -c "$username" /etc/passwd' = "1" ]] #check if username exists
                                then
                                        grep "$username" /etc/passwd
                                        grep "$username" /etc/shadow
                                        grep "$username" /etc/group
                                else
                                        echo "$username not found"
                                fi
                        #echo "Enter username"
                        #read username
                        #done
        else
                echo "Password file not present"
        fi
else
        echo "You must be root to run this script"
fi
if [[ 'grep -c "$username" /etc/passwd' = "1" ]]

'grep -c "$username" /etc/passwd' will never equal "1", because it's a string, containing the letters grep -c "$username" /etc/passwd" . Single-quotes never run what's inside them.

Even if you did run it by putting it in backticks instead of single quotes, you'd still be doing it rather the long way around, making it count and checking its output string instead of just checking the return value grep and any other shell program gives you every time they finish. If grep succeeds, it'll trigger like 'true' in an if-statement, if it fails, it'll be like 'false'.

How about:

if grep -q "^$username:" /etc/passwd
then
...
fi

The "^" at the beginning of the string has a special meaning to grep, "must start at the beginning of the line". The : at the end just means :, so we know it matched the entire username part of "username:x:uid:gid:..." and not just part of it like "usernamezzz:x:uid:gid:..."

---------- Post updated at 05:05 PM ---------- Previous update was at 04:54 PM ----------

You also don't need to nest things 9 if-statements deep to check more than one thing in a row. Do that for more than 3 things and your code will become unreadable. If something's wrong, just quit early and spare yourself the mess.

You shouldn't depend on the $HOME variable. In some situations that's not even set. Try UID, that'll be 0 for root.

function die
{
        echo "$@" >&2
        exit 1
}

[ "$UID" -eq 0 ] || die "Not root"
[ -f /etc/passwd ] || die "/etc/passwd missing"

read USERNAME

until [ "$USERNAME" = "done" ]
do
        if grep -q "^${USERNAME}:" /etc/passwd
        then
                ...
        else
                echo "$USERNAME not found"
        fi

        read USERNAME
done
1 Like

Corona688's grep -q solution is the best, but so you understand how things work I'll try and explain how one gets to it.

  1. call grep, and throw away it's output and then examine return status:
grep "^$username:" /etc/passwd > /dev/null
if [ $? -eq 0 ]
then
     # Found string
    ...
fi

Grep (and most unix commands) set exit status ($?) to zero on sucess (ie expresion matches), and non-zero for failure (expression not found or other errors, eg could not open file)

  1. Grep supports a -q option (be quiet and just set exit status), so we can simplify to:
grep -q "^$username:" /etc/passwd
if [ $? -eq 0 ]
then
     # Found string
    ...
fi
  1. if runs command and executes block when command result is zero ( [ is just a shortcut for the test command). So we can use if to directly test the grep result:
if grep -q "^$username:" /etc/passwd
then
    # Found string
    ...
fi
1 Like

Oh I see, I didn't click that my statement was being read as a string and would always fail. Makes sense. Still new with all this.

2 more questions. Now my until loop exits when the user enters 'done'. I want it to exit when the user enter EOF (^D). Been looking around and can't seem to find the right way to do it. Tried putting it = ^D but that opened a can of worms.

Also, every time the search fails, what would be my best way to store all the failed accounts in a comma-separated list variable I could display at the end when the user exits the loop and script.

Thanks all for the help I appreciate.

Something like this will stop when the user enters CTL-D and illustrates saving failed names:

list=""
while read username
do
   list="$list,$username"  # save list of names, introduces a leading comma
done

echo "${list#,}   #ditch the leading comma
1 Like