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