前言:
略懂编程的都知道,if else只能进其一,满足if条件就不会进else,
然而在shell学习过程中,却遇到了,满足了if条件后,依然执行了else中的代码块。
本案例所有代码均在:/root/myshell/for 目录下进行,案例中不再累述。
注:“批量删除用户”和问题无关,属于扩展内容。
概述:问题的根源在用shell写的不严谨,另外shell解释器本身也存在问题(应该是个bug)
一、准备工作(切换目录,并创建测试数据)
1.创建测试用户users.txt,并向其中写入测试数据
注:t是一条特殊数据。
#cd /root/myshell/for
#vim users.txt
t
centos
suse
huawei
二、批量添加用户
1.编辑脚本
#vim adduser.sh
!/bin/bash
userList=$(cat /root/myshell/for/users.txt)
for user in $userList
do
user_a=$(grep "$user" /etc/passwd | awk -F : '{print $1}' ) #不严谨的写法
#user_a=$(egrep "^\<$user\>" /etc/passwd | awk -F : '{print $1}' ) #严谨的写法
#user_a=$(getent passwd $user| awk -F : '{print $1}' ) #严谨的写法
if [[ $user_a == $user ]]
then echo "user:$user is already exists"
else
useradd $user
echo "123456" | passwd --stdin $user
echo "user:$user has been add and passwd has been set"
fi
done
2.测试脚本(问题所在,t用户同时执行了if和else中的代码块)
#ls /home
#sh adduser.sh #第一次批量添加(一切正常)
#ls /home
#sh adduser.sh #第二次批量添加,所有用户都应该被提示已存在的,偏偏就t用户,一方面进入了if被提示已存在,一方面又进入了else执行了else的代码块。
3.问题解决(再次验证)
#user_a=$(grep "$user" /etc/passwd | awk -F : '{print $1}' ) #不严谨写法(问题根源)
#user_a=$(egrep "^\<$user\>" /etc/passwd | awk -F : '{print $1}' ) #严谨写法
user_a=$(getent passwd $user| awk -F : '{print $1}' ) #严谨写法
经过一番求助,百度未果,在Q群好友的协助下,问题得以解决,再次验证。一切正常。
具体步骤:把上面三行代码中的第一行注释掉,下面两行都是严谨写法,任选其一即可。
4.问题分析
1、首先,略懂编程的都知道,if和else不会同时进入,执行里面的代码的,至少java语言不会遇到这种情况。
但是,shell脚本同一个命令,确实可以同时被if和else里面的代码块执行,本案例就是个奇葩的证明。
定论:这足以说明,/bin/bash的shell解释器是不走寻常路,能同时执行if和else里的代码,应该是个bug。
2、遇到问题,还是要想办法解决的。和度娘一番探讨后,英雄磨鞋底,未果,求助QQ群好友,最终得以解决。具体分析一下这两行严谨的代码:
#user_a=$(grep "$user" /etc/passwd | awk -F : '{print $1}' ) #不严谨写法(问题根源)
#user_a=$(egrep "^\<$user\>" /etc/passwd | awk -F : '{print $1}' ) #严谨写法(正则过滤)
user_a=$(getent passwd $user| awk -F : '{print $1}' ) #严谨写法
第一行:之所以会被同时进入if和else,是应该在进行for循环的时候,t用户通过grep查找,被匹配到了,不严谨之处是,grep并不是精准匹配,所以会匹配到含有t字母的用户。所以会进入if代码块,被提示t用户已存在。
然而,t用户依然满足else,因为站在grep不是精准匹配这个角度考虑,tom这个用户也可能会被过滤出来。
也就是if语句的条件判断,最终由两个形态:
if [[ $user_a == $user ]]
if tom ==t #站在这个角度,返回结果自然是false,自然会进入else
if t == t #站在这个角度,返回结果自然是true,自然会进入if
奇葩之处就在这里了。
3、后两行严谨的写法,都是精准查找,第二行是通过正则表达式完全匹配,第三者也是完全匹配。所以,不会出现查找过滤出tom,出现 if tom==t 返回false的情况,只会查找到 if t==t返回true。
注:上图之所以看到grep "t" 过滤出的用户名中不包含t,依然被过滤了出来,是因为该用户所在数据的行中出现了t,因为管道过滤有先后的问题,调换一下管道过滤顺序,就一目了然了。
三、批量删除用户(简略版)
注:由于脚本比较简单,作为扩展内容,不再过多累述。
1.编辑脚本
#vim deluser.sh
#!/bin/bash
delList=$(cat /root/myshell/for/users.txt)
for user in $delList
do
userdel -r $user >> /dev/null
if [ $? -eq 0 ]
then
echo "user :$user has been deleted"
else
echo "user :$user may be not exists,delete fail"
fi
done
2.验证脚本
#ls /home 查看home目录下,刚才新建的用户跟目录
#cat /etc/passwd | tail -5 也可以查看
#sh deluser.sh 执行批量删除命令
#ls /home 再次查看home目录
批量删除的过程,很顺利。
四、批量删除用户(for循环嵌套if)
设计思路:
删除前,先判断被删除用户是否存在。然后再根据是否删除成功,给予不同的提示。
注:删除前需要在users.txt中特意添加一个不存在的用户tom,便于看测试效果。
1.编辑脚本
#vim del.sh
#!/bin/bash
delList=$(cat /root/myshell/for/users.txt)
for user in $delList
do
user_a=$(getent passwd $user | awk -F : '{print $1}')
if [ "$user_a" != "$user" ]
then
echo "user:$user isn't exit"
else
userdel -r $user >> /dev/null
if [ $? -eq 0 ]
then "user:$user has been delete succfully!"
else
echo "user:$user fail to be delete "
fi
fi
done
2.脚本验证
#sh del.sh
后记:
在此,再次感谢“Linux运维交流与招聘(859407315)”群的“需谨慎几十年(1827617524) ”的耐心指导。