我有一个bash shell脚本,该脚本循环遍历服务器列表文件以验证该服务器上是否存在用户。我的问题是,我希望脚本在执行命令之前回显用户所在的服务器:明确; main_sec
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
您想要保存一个匹配项,以后再使用。您可以为此使用变量:
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
这里是您的代码的正确版本,下面是我的所有建议:
#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 '%s\n' \
"${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
fi
可以避免的一些代码错误:
$?
不会改变!您第二次执行的$?
将为0,因为它是if
的返回代码(除非您更改代码)代替语法command; [...] if [ $? ]
,请使用:
if ssh ...; then
...
else
...
fi
https://mywiki.wooledge.org/BashPitfalls#pf44
[
[
或test
是POSIX测试命令。它可以对文件和字符串进行简单的测试。在bash中,您应使用功能更强大的[[
并为保持一致性而禁止[
。 [[
可以进行模式匹配,使用起来更快捷,更安全。
http://mywiki.wooledge.org/BashGuide/TestsAndConditionals
http://mywiki.wooledge.org/BashFAQ/031
[main_sec
将启动其他内容,并且您不希望脚本结束后的代码继续执行,因此您应在之后添加一个退出以确保在main_sec
完成时真正终止
egrep
已过时如果需要,请使用grep -E
(此处不需要)
grep ... > /dev/null
如果只需要返回码,请使用grep -q
。当您只想知道是否存在匹配项时,这可以避免扫描所有输入。它有助于获得更大的输入。
getent
代替/ etc / passwd而不是扫描/ etc / passwd,应使用为此专门设计的getent
如果有帮助,它也涵盖域帐户。
按照惯例,我们将环境变量(PAGER
,EDITOR
,..)和内部外壳变量(SHELL
,BASH_VERSION
,..)大写。所有其他变量名应小写。请记住,变量名称区分大小写;该约定避免了意外覆盖环境和内部变量。
echo
与选项标志一起使用echo
输出一个字符串。 echo
具有许多可移植性问题,因此切勿与选项标志一起使用。考虑改为printf
:printf 'name: %s\n' "$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
在这种情况下,您正在使用echo -e '$RED ... $NONE'
。
我猜是因为RED='\e[31m' NONE='\e(B\e[0m'
如果改为使用这些值,则可以使用没有echo
的-e
的颜色:
RED=$'\e[31m' NONE=$'\e(B\e[0m'
[此处,username
可能由不希望使用; rm -rf /
等值的人设置。在ssh服务器上启动后,这可能是灾难性的。即使您自己在代码中设置了用户名,您也不知道该代码将来是否会更改为某种形式的用户定义值,因此您应该保护代码免受此侵害,同时仍然可以在将特殊字符传递给ssh之前先进行转义。有很多方法可以做到这一点,其中一种是:
printf 'getent passwd %q >/dev/null' "$username" | ssh -q -o StrictHostKeyChecking=no "$server" bash -s