Skip to content
Advertisement

bash shell script for loop

I have a bash shell script that loops through a server list file to verify a user exists on that server. My question is, I want the script to echo out which servers the user exists on before executing command: sleep; clear; main_sec <(this is a function) problem is as soon as that condition is met, it just echo’s the first server met then executes code. How do i echo it all out then execute the code?

echo -e "${YELLOW}Checking if user account exists on any of the servers above${NONE}"

readarray -t lines < servers.txt

for server in "${lines[@]}"; do
    ssh -q -o StrictHostKeyChecking=no $server "egrep "^$username" /etc/passwd" &>/dev/null

    if [ $? -eq 1 ]; then
        continue
    fi

    if [ $? -eq 0 ]; then
        echo -e "${RED}User account $username already exists on $server, Must choose a unique username before proceeding.  Returning to menu...${NONE}"
sleep 5; clear; main_sec 
fi
done

Advertisement

Answer

What you want is to save a match and use it later. You can use a variable for that:

return_menu=
for ...; do
  if ssh ...; then
    ...
  else
    echo >&2 "User account $username already exists on $server, Must choose a unique username before proceeding. Returning to menu..."
    return_menu=return_menu
  fi
fi
done
if [[ $return_menu != '' ]]; then
  sleep 5
  clear
  main_sec
fi

Here is a corrected version of your code with all my advice from below:

#username=...
return_menu=
echo "${YELLOW}Checking if user account exists on any of the servers above$NONE"
readarray -t lines < servers.txt
for server in "${lines[@]}"; do
  if printf 'getent passwd %q >/dev/null' "$username" |
      ssh -q -o StrictHostKeyChecking=no "$server" bash -s; then
    continue
  else
    printf >&2 '%sn' 
      "${RED}User account $username already exists on $server." 
      "You must choose a unique username before proceeding." 
      "${NONE}Returning to menu..."
    return_menu=return_menu
fi
done
if [[ $return_menu != '' ]]; then
  sleep 5
  clear
  main_sec
  exit 0
fi

Some code mistakes you can avoid:

Don’t assume $? won’t change!

Your second execution of $? will be 0 as it’s if‘s return code (unless you change your code) Instead of the syntax command; if [ $? ], use:

if ssh ...; then
  ...
else
  ...
fi

https://mywiki.wooledge.org/BashPitfalls#pf44

Avoid [

[ or test is the POSIX test command. It can do simple tests on files and strings. In bash, you should use the more powerful [[ instead and ban [ for sake of consistency. [[ can do pattern matching, is faster and safer to use.

http://mywiki.wooledge.org/BashGuide/TestsAndConditionals

http://mywiki.wooledge.org/BashFAQ/031

Termination

main_sec will launch something else, and you don’t want lines further down the script continue executing once you finish so you should add an exit afterwards to be sure you really terminate when main_sec finishes

egrep is deprecated

Use grep -E if needed (not needed here)

Don’t do grep ... > /dev/null

If you only want the return code, use grep -q. This avoid scanning all the input when you only want to know if a match is there. It helps for larger inputs.

Consider using getent instead of /etc/passwd

Instead of scanning /etc/passwd, you should use getent which is designed for it It also covers domain accounts, if it helps.

Capital variable names

By convention, we capitalize environment variables (PAGER, EDITOR, ..) and internal shell variables (SHELL, BASH_VERSION, ..). All other variable names should be lower case. Remember that variable names are case-sensitive; this convention avoids accidentally overriding environmental and internal variables.

Don’t use echo with options flags

echo outputs a string. echo has many portability problems, and should never be used with option flags. Consider printf instead: printf 'name: %sn' "$name".

http://wiki.bash-hackers.org/commands/builtin/echo

http://cfajohnson.com/shell/cus-faq.html#Q0b

http://www.in-ulm.de/~mascheck/various/echo+printf

In this case, you are using echo -e '$RED ... $NONE'.

I’m guessing it’s because RED='e[31m' NONE='e(Be[0m'

If you used these values instead, you could use the colors without echo‘s -e:

RED=$'e[31m' NONE=$'e(Be[0m'

Don’t assume variables will not contain special characters

Here, username may be set by someone who has ill intent to values such as ; rm -rf /. Once launched on you ssh servers, this could be disastrous. Even if you have set the username in the code yourself, you don’t know if the code won’t be changed in the future to some form of user-defined value, so you should protect your code against this while you still can by escaping special characters before passing them to ssh. There are many ways to do that, and one is:

printf 'getent passwd %q >/dev/null' "$username" | ssh -q -o StrictHostKeyChecking=no "$server" bash -s
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement