六、 循环结构和分支结构
1.for循环结构
列表式循环
列表for循环:用于将一组命令执行**已知的次数**
根据变量的不同取值,重复执行不同序列
for语法格式 for 变量名 in 变量值列表; do 当变量值列表有值赋给变量名时执行的语句块 done
或者:
for variable in {list} do command command … done 或者 for variable in a b c do command command done
举例说明
# for var in {1..10};do echo $var;done
# for var in 1 2 3 4 5;do echo $var;done
# for var in `seq 10`;do echo $var;done
# for var in $(seq 10);do echo $var;done
# for var in {0..10..2};do echo $var;done
# for var in {2..10..2};do echo $var;done
# for var in {10..1};do echo $var;done
# for var in {10..1..-2};do echo $var;done
# for var in `seq 10 -2 1`;do echo $var;done
例子1:
#!/bin/bash #for i in 1 2 3 4 5 6 7 8 9 10 #for i in `head /etc/passwd` for i in `ls /shell/*.sh` do echo $i done
把192.168.1.1~192.168.1.5主机的ip地址输出到屏幕上
#!/bin/bash mynet=192168.1. #for i in 1 2 3 4 5 #for i in `seq 254` #for i in `30 45` #for i in `seq 10 2 30` #for i in {a..z} for i in {1..10} do echo $mynet$i done
例子2:
#!/bin/bash for i in `seq 10` do if [ `expr $i % 2` -eq 0 ];then echo $i fi done
用for循环输出192.168.1.0/24网段内所有主机的ip地址
#!/bin/bash for I in `seq 1 254` do echo 192.168.1.$I done
把自己不能与其通信的本网段的主机的ip地址输出到屏幕上
并统计不能与其通信的主机的个数
#!/bin/bash j=0 mynet=192.168.1 for i in `seq 5` do ping -c 3 $mynet.$i &> /dev/null if [ $? -ne 0 ];then echo "$mynet.$i" let j++ fi done echo "不在的主机有 $j 台"
例子3:
#!/bin/bash mynet=192.168.1 for i in `seq 5` do (ping -c 3 $mynet.$i &> /dev/null if [ $? -ne 0 ];then echo "主机 $mynet.$i not line!!!" fi) & done
注意:
() 把当前的程序放到子shell里执行
& 把程序置入后台运行
把1-20之间的偶数输出 并统计奇数的个数。
#!/bin/bash for s in `seq 20` do if [ `expr $s % 2` -eq 0 ];then echo $s let j++ fi done echo "共有$j个奇数"
把系统已有的binlog日志文件输出
方法1
#!/bin/bash mysqldir=/var/lib/mysql cd $mysqldir for i in `cat localhost-bin.index` do binlogname=`basename $i` echo $binlogname done
方法2:
#!/bin/bash for binlog in `cat /var/lib/mysql/data-bin.index` do echo $binlog done
方法3:
#!/bin/bash for binlog in `cat /var/lib/mysql/data-bin.index` do binlogname=`basename $binlog` echo $binlogname done
每天的 23:30 备份当前数据库服务器上新生成的binlog日志文件到系统的/newbinlogdir目录里. (当前正在使用binlog日志文件不备份)
#!/bin/bash datadir=/var/lib/mysql bakdir=/mysqlbak lastbinlogname=`tail -1 $datadir/mysqld-bin.index | awk -F"/" '{print $2}'` if [ -e $bakdir ];then cd $datadir for i in `cat mysqld-bin.index ` do binlogname=`basename $i` if [ "$binlogname" != "$lastbinlogname" ];then if [ ! -e $bakdir/$binlogname ];then cp $binlogname $bakdir else echo "$binlogname baked" fi else echo "$lastbinlogname useeing!!!!" fi done else mkdir $bakdir cd $datadir for i in `cat mysqld-bin.index ` do binlogname=`basename $i` if [ "$binlogname" != "$lastbinlogname" ];then if [ ! -e $bakdir/$binlogname ];then cp $binlogname $bakdir else echo "$binlogname baked" fi else echo "$lastbinlogname useeing!!!!" fi done fi
㈡ 不带列表循环
不带列表的for循环执行时由用户指定参数和参数的个数
基本语法格式
for variable
do
command
command
…
done
举例说明
#!/bin/bash
for var
do
echo $var
done
echo "脚本后面有$#个参数"
(三)类C语言风格的for循环
基本语法结构
for(( expr1;expr2;expr3 ))
do
command
command
…
done
说明:
expr1:定义变量并赋初值
expr2:决定是否进行循环(条件);即条件判断
expr3:决定循环变量如何改变,决定循环什么时候退出;即步长
例子:
for (( i=1;i<=5;i++)) do echo $i done
举例说明
# for ((i=1;i<=5;i++));do echo $i;done
# for ((i=1;i<=10;i+=2));do echo $i;done
# for ((i=2;i<=10;i+=2));do echo $i;done
例子:
for((i=5;i==3;i++)) do echo $i sleep 1 done
使用for循环嵌套实现九九乘法表
#!/bin/bash #通过for循环嵌套实现九九乘法表,其中temp用于保存i+j的值 for (( i = 1;i <=9; i++ )) do #內层循环 for (( j=1; j <=i; j++ )) do let "temp = i * j" #暂时存储i*j的值 echo -n "$i*$j=$temp " done echo "" done
使用for循环嵌套实现8X8个的棋盘
#!/bin/bash #外层循环 for (( i = 1;i <=8; i++ )) do #內层循环 for (( j=1; j <=8; j++ )) do #下面两行用于不重叠显示黑白格 total=$(( $i + $j )) tmp=$(( $total % 2 )) if [ $tmp -eq 0 ]; then echo -e -n "\033[47m " #显示白格 else echo -e -n "\033[40m " #显示黑格 fi done echo "" #换行 done
2. 应用案例
㈠ 脚本计算1-100奇数和
① 思路
定义一个变量来保存奇数的和 sum=0 找出1-100的奇数,保存到另一个变量里 i,i=遍历出来的奇数 从1-100中找出奇数后,再相加,然后将和赋值给变量 循环变量 for 遍历完毕后,将sum的值打印出来
② 落地实现(条条大路通罗马)
#!/bin/bash
#定义一个变量来保存奇数的和
sum=0
#打印1-100的奇数并且相加重新赋值给sum
for i in {1..100..2}
do
sum=$[ $i + $sum ]
done
#打印1-100的奇数和
echo "1-100的奇数和为:$sum"
#!/bin/bash
#定义一个变量来保存奇数的和
sum=0
#打印1-100的奇数并且相加重新赋值给sum
for (( i=1;i<=100;i+=2))
do
let sum=sum+$i
或者
let sum=sum+i
或者
let sum=$sum+$i
done
#打印1-100的奇数和
echo "1-100的奇数和为:$sum"
#!/bin/bash
sum=0
for ((i=1;i<=100;i++))
do
if [ $[$i%2] -ne 0 ];then
let sum=sum+$i
fi
done
echo "1-100的奇数和是:$sum"
#!/bin/bash
sum=0
for ((i=1;i<=100;i++))
do
[ $[$i%2] -eq 0 ] && true || let sum=sum+$i
done
echo "1-100的奇数和是:$sum"
延伸:
true 真
: 真
false 假
方法1:
#!/bin/bash
sum=0
for i in {1..100..2}
do
sum=$[$i+$sum]
done
echo "1-100的奇数和为:$sum"
方法2:
#!/bin/bash
sum=0
for ((i=1;i<=100;i+=2))
do
let sum=$i+$sum
done
echo "1-100的奇数和为:$sum"
方法3:
#!/bin/bash
sum=0
for ((i=1;i<=100;i++))
do
if [ $[$i%2] -ne 0 ];then
let sum=$sum+$i
fi
或者
test $[$i%2] -ne 0 && let sum=$sum+$i
done
echo "1-100的奇数和为:$sum"
方法4:
sum=0
for ((i=1;i<=100;i++))
do
if [ $[$i%2] -eq 0 ];then
continue
else
let sum=$sum+$i
fi
done
echo "1-100的奇数和为:$sum"
#!/bin/bash
sum=0
for ((i=1;i<=100;i++))
do
test $[$i%2] -eq 0 && continue || let sum=sum+$i
done
echo "1-100的奇数和是:$sum"
循环控制语句
循环体:do....done之间的内容
continue:继续;表示循环体内下面的代码不执行,重新开始下一次循环
break:打断;马上停止执行本次循环,执行循环体后面的代码
exit:表示直接跳出程序
[root@server ~]# cat for5.sh
#!/bin/bash
for i in {1..5}
do
test $i -eq 2 && break || touch /tmp/file$i
done
echo hello hahahah
㈡ 判断所输整数是否为质数
质数(素数):只能被1和它本身整除的数叫质数。 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
① 思路
让用户输入一个数,保存到一个变量里 read -p "请输入一个正整数:" num 如果能被其他数整除就不是质数——>$num%$i是否等于0 $i=2到$num-1 如果输入的数是1或者2取模根据上面判断又不符合,所以先排除1和2 测试序列从2开始,输入的数是4——>得出结果$num不能和$i相等,并且$num不能小于$i
② 落地实现
#!/bin/env bash
#定义变量来保存用户所输入数字
read -p "请输入一个正整数字:" number
#先排除用户输入的数字1和2
[ $number -eq 1 ] && echo "$number不是质数" && exit
[ $number -eq 2 ] && echo "$number是质数" && exit
#循环判断用户所输入的数字是否质数
for i in `seq 2 $[$number-1]`
do
[ $[$number%$i] -eq 0 ] && echo "$number不是质数" && exit
done
echo "$number是质数"
优化思路:没有必要全部产生2~$[$number-1]序列,只需要产生一半即可。
更好解决办法:类C风格完美避开了生成序列的坑
for (( i=2;i<=$[$number-1];i++))
do
[ $[$number%$i] -eq 0 ] && echo "$number不是质数" && exit
done
echo "$number是质数"
㈢ 批量创建用户
**需求:**批量加5个新用户,以u1到u5命名,并统一加一个新组,组名为class,统一改密码为123
① 思路
添加用户的命令 useradd -G class 判断class组是否存在 grep -w ^class /etc/group 或者groupadd class 根据题意,判断该脚本循环5次来添加用户 for 给用户设置密码,应该放到循环体里面
② 落地实现
#!/bin/env bash
#判断class组是否存在
grep -w ^class /etc/group &>/dev/null
test $? -ne 0 && groupadd class
#循环创建用户
for ((i=1;i<=5;i++))
do
useradd -G class u$i
echo 123|passwd --stdin u$i
done
方法一:
#!/bin/bash
#判断class组是否存在
grep -w class /etc/group &>/dev/null
[ $? -ne 0 ] && groupadd class
#批量创建5个用户
for i in {1..5}
do
useradd -G class u$i
echo 123|passwd --stdin u$i
done
方法二:
#!/bin/bash
#判断class组是否存在
cut -d: -f1 /etc/group|grep -w class &>/dev/null
[ $? -ne 0 ] && groupadd class
#循环增加用户,循环次数5次,for循环,给用户设定密码
for ((i=1;i<=5;i++))
do
useradd u$i -G class
echo 123|passwd --stdin u$i
done
方法三:
#!/bin/bash
grep -w class /etc/group &>/dev/null
test $? -ne 0 && groupadd class
或者
groupadd class &>/dev/null
for ((i=1;i<=5;i++))
do
useradd -G class u$i && echo 123|passwd --stdin u$i
done
3. 课堂练习
㈠ 批量创建用户
**需求1:**批量新建5个用户stu1~stu5,要求这几个用户的家目录都在/rhome.
#!/bin/bash
#判断/rhome是否存在
[ -f /rhome ] && mv /rhome /rhome.bak
test ! -d /rhome && mkdir /rhome
或者
[ -f /rhome ] && mv /rhome /rhome.bak || [ ! -d /rhome ] && mkdir /rhome
#创建用户,循环5次
for ((i=1;i<=5;i++))
do
useradd -d /rhome/stu$i stu$i
echo 123|passwd --stdin stu$i
done
㈡ 局域网内脚本检查主机网络通讯
需求2:
写一个脚本,局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里
以10.1.1.1~10.1.1.10为例
10.1.1.1~10.1.1.254
#!/bin/bash
#定义变量
ip=10.1.1
#循环去ping主机的IP
for ((i=1;i<=10;i++))
do
ping -c1 $ip.$i &>/dev/null
if [ $? -eq 0 ];then
echo "$ip.$i is ok" >> /tmp/ip_up.txt
else
echo "$ip.$i is down" >> /tmp/ip_down.txt
fi
或者
[ $? -eq 0 ] && echo "$ip.$i is ok" >> /tmp/ip_up.txt || echo "$ip.$i is down" >> /tmp/ip_down.txt
done
[root@server shell03]# time ./ping.sh
real 0m24.129s
user 0m0.006s
sys 0m0.005s
延伸扩展:shell脚本并发
并行执行:
{程序}&表示将程序放到后台并行执行,如果需要等待程序执行完毕再进行下面内容,需要加wait
#!/bin/bash
#定义变量
ip=10.1.1
#循环去ping主机的IP
for ((i=1;i<=10;i++))
do
{
ping -c1 $ip.$i &>/dev/null
if [ $? -eq 0 ];then
echo "$ip.$i is ok" >> /tmp/ip_up.txt
else
echo "$ip.$i is down" >> /tmp/ip_down.txt
fi
}&
done
wait
echo "ip is ok...."
[root@server ~]# time ./ping.sh
ip is ok...
real 0m3.091s
user 0m0.001s
sys 0m0.008s
㈢ 判断闰年
需求3:
输入一个年份,判断是否是润年(能被4整除但不能被100整除,或能被400整除的年份即为闰年)
#!/bin/bash
read -p "Please input year:(2017)" year
if [ $[$year%4] -eq 0 -a $[$year%100] -ne 0 ];then
echo "$year is leap year"
elif [ $[$year%400] -eq 0 ];then
echo "$year is leap year"
else
echo "$year is not leap year"
fi
while循环
while中的动作有可能一次都不执行
特点:条件为真就进入循环;条件为假就退出循环
while语法
while 条件测试 do 命令序列 done
或者:
while 表达式 do command... done while [ 1 -eq 1 ] 或者 (( 1 > 2 )) do command command ... done
若条件表达式为真,重复执行动作,直到条件表达式为假才结束
do while
do while将动作前置,先执行前置,先执行动作,再判断条件表达式。因此do while中的动作至少执行一次。
do 动作 while (条件表达式)
使用while循环输出数字1到10(升序输出)降序输出
[root@localhost shell]# cat while10.sh #!/bin/bash j=1 while [ $j -le 10 ] do echo $j let j++ #j=j+1 done
批量添加10个系统用户要求如下:
1、用户的密码与用户名相同
2、用户的前缀是user
3、强制新添加的用户首登录系统时,重置自己的登录系统的密码。
4、编写脚本userdel.sh (删除刚才批量添加的10个系统用户)
计算100以内所有正整数的和
#!/bin/bash declare -i I=1 declare -i SUM=0 while [ $I -le 100 ]; do let SUM+=$I let I++ done echo $SUM
练习:转换用户输入的字符为大写,除了quit:
#!/bin/bash # read -p "Input something: " STRING while [ $STRING != 'quit' ]; do echo $STRING | tr 'a-z' 'A-Z' read -p "Input something: " STRING done
练习:每隔5秒查看hadoop用户是否登录,如果登录,显示其登录并退出;否则,显示当前时间,并说明hadoop尚未登录:
#!/bin/bash # who | grep "hadoop" &> /dev/null RETVAL=$? while [ $RETVAL -ne 0 ]; do echo "`date`, hadoop is not log." sleep 5 who | grep "hadoop" &> /dev/null RETVAL=$? done echo "hadoop is logged in."
写一个脚本:
1. 显示一个菜单给用户:
d|D) show disk usages. m|M) show memory usages. s|S) show swap usages. *) quit.
2 当用户给定选项后显示相应的内容;
扩展:
当用户选择完成,显示相应信息后,不退出;而让用户再一次选择,再次显示相应内容;除了用户使用quit;
#!/bin/bash # cat << EOF d|D) show disk usages. m|M) show memory usages. s|S) show swap usages. *) quit. EOF read -p "Your choice: " CHOICE while [ $CHOICE != 'quit' ];do case $CHOICE in d|D) echo "Disk usage: " df -Ph ;; m|M) echo "Memory usage: " free -m | grep "Mem" ;; s|S) echo "Swap usage: " free -m | grep "Swap" ;; *) echo "Unknown.." ;; esac read -p "Again, your choice: " CHOICE done
2. 应用案例
㈠ 脚本计算1-50偶数和
#!/bin/env bash
sum=0
for ((i=0;i<=50;i+=2))
do
let sum=$sum+$i (let sum=sum+i)
done
echo "1-50的偶数和为:$sum"
#!/bin/bash
#定义变量
sum=0
i=2
#循环打印1-50的偶数和并且计算后重新赋值给sum
while [ $i -le 50 ]
do
let sum=$sum+$i
let i+=2 或者 $[$i+2]
done
#打印sum的值
echo "1-50的偶数和为:$sum"
㈡ 脚本同步系统时间
① 具体需求
写一个脚本,30秒同步一次系统时间,时间同步服务器10.1.1.1 如果同步失败,则进行邮件报警,每次失败都报警 如果同步成功,也进行邮件通知,但是成功100次才通知一次
② 思路
- 每个30s同步一次时间,该脚本是一个死循环 - while true;do 同步时间,然后休息30s(sleep 30)done - 同步失败发送邮件 - 在do.....done循环体之间加if...else...(判断同步失败还是成功) - 同步成功100次发送邮件 - 统计成功次数——>count=0——>成功1次加+1——>let count++
③ 落地实现
#!/bin/env bash
# 该脚本用于时间同步
NTP=10.1.1.1
count=0
while true
do
ntpdate $NTP &>/dev/null
if [ $? -ne 0 ];then
echo "system date failed" |mail -s "check system date" root@localhost
else
let count++
if [ $count -eq 100 ];then
echo "system date success" |mail -s "check system date" root@localhost && count=0
fi
fi
sleep 30
done
#!/bin/bash
#定义变量
count=0
ntp_server=10.1.1.1
while true
do
rdate -s $ntp-server &>/dev/null
if [ $? -ne 0 ];then
echo "system date failed" |mail -s 'check system date' root@localhost
else
let count++
if [ $[$count%100] -eq 0 ];then
echo "system date successfull" |mail -s 'check system date' root@localhost && count=0
fi
fi
sleep 3
done
以上脚本还有更多的写法,课后自己完成
until循环结构
与while的执行条件相反
当条件不成立时,循环执行语句序列
特点:条件为假就进入循环;条件为真就退出循环
语法:
until 条件判断表达式 do 循环体 done
或者:
until expression [ 1 -eq 1 ] (( 1 >= 1 )) do command command ... done
例子:
until [1 -eq 1 ] #条件一直成立,会一直执行循环体,就是死循环 do echo "test1" sleep 1 done
打印1-5数字
i=1
while [ $i -le 5 ]
do
echo $i
let i++
done
i=1
until [ $i -gt 5 ]
do
echo $i
let i++
done
auto_check_gw.sh 自动切换网关脚本
2. 应用案例
###㈠ 具体需求
使用until语句批量创建10个用户,要求stu1—stu5用户的UID分别为1001—1005; stu6~stu10用户的家目录分别在/rhome/stu6—/rhome/stu10
㈡ 思路
创建用户语句 useradd -u|useradd -d
使用循环语句(until)批量创建用户 until循环语句结构
判断用户前5个和后5个 条件判断语句
㈢ 落地实现
#!/bin/env bash
if [ -d /rhome ];then
echo "/rhome目录已存在"
else
mkdir /rhome
echo "/rhome不存在,已完成创建"
fi
i=1
until [ $i -gt 10 ]
do
if [ $i -le 5 ];then
useradd -u $[1000+$i] stu$i
echo 123|passwd --stdin stu$i
else
useradd -d /rhome/stu$i stu$i
echo 123|passwd --stdin stu$i
fi
let i++
done
==================================================
#!/bin/bash
i=1
until [ $i -gt 10 ]
do
if [ $i -le 5 ];then
useradd -u $[1000+$i] stu$i && echo 123|passwd --stdin stu$i
else
[ ! -d /rhome ] && mkdir /rhome
useradd -d /rhome/stu$i stu$i && echo 123|passwd --stdin stu$i
fi
let i++
done
四、课后作业
1.判断/tmp/run目录是否存在,如果不存在就建立,如果存在就删除目录里所有文件 2.输入一个路径,判断路径是否存在,而且输出是文件还是目录,如果是链接文件,还得输出是 有效的连接还是无效的连接 3.交互模式要求输入一个ip,然后脚本判断这个IP 对应的主机是否 能ping 通,输出结果类似于: Server 10.1.1.20 is Down! 最后要求把结果邮件到本地管理员root@localhost mail01@localhost 4.写一个脚本/home/program,要求当给脚本输入参数hello时,脚本返回world,给脚本输入参数world时,脚本返回hello。而脚本没有参数或者参数错误时,屏幕上输出“usage:/home/program hello or world” 5.写一个脚本自动搭建nfs服务
1 12 123 1234 12345 X轴: for ((i=1;i<=5;i++));do echo -n $i;done Y轴: 负责打印换行 #!/bin/bash for ((y=1;y<=5;y++)) do for ((x=1;x<=$y;x++)) do echo -n $x done echo done #!/bin/bash for ((y=1;y<=5;y++)) do x=1 while [ $x -le $y ] do echo -n $x let x++ done echo done
demo2:打印如下图案
5 54 543 5432 54321 Y轴:打印换行 X轴:打印数字 5-1 #!/bin/bash y=5 while (( $y >= 1 )) do for ((x=5;x>=$y;x--)) do echo -n $x done echo let y-- done #!/bin/bash for (( y=5;y>=1;y--)) do for (( x=5;x>=$y;x--)) do echo -n $x done echo done #!/bin/bash y=5 while [ $y -ge 1 ] do for ((x=5;x>=$y;x--)) do echo -n $x done echo let y-- done #!/bin/bash y=1 until (( $y >5 )) do x=1 while (( $x <= $y )) do echo -n $[6-$x] let x++ done echo let y++ done 课后打印: 54321 5432 543 54 5
课堂练习:打印九九乘法表(三种方法)
1 12 123 1234 12345 for ((y=1;y<=5;y++)) do for ((x=1;x<=$y;x++)) do echo -n $x done echo done 1*1=1 1*2=2 2*2=4 1*3=3 2*3=6 3*3=9 1*4=4 2*4=8 3*4=12 4*4=16 1*5=5 2*5=10 3*5=15 4*5=20 5*5=25 1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36 1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49 1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64 1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81 Y轴:循环9次,打印9行空行 X轴:循环次数和Y轴相关;打印的是X和Y轴乘积 $[] $(()) #!/bin/bash for ((y=1;y<=9;y++)) do for ((x=1;x<=$y;x++)) do echo -ne "$x*$y=$[$x*$y]\t" done echo echo done #!/bin/bash y=1 while [ $y -le 9 ] do x=1 while [ $x -le $y ] do echo -ne "$x*$y=$[$x*$y]\t" let x++ done echo echo let y++ done 或者 #!/bin/bash for i in `seq 9` do for j in `seq $i` do echo -ne "$j*$i=$[$i*$j]\t" done echo echo done 或者 #!/bin/bash y=1 until [ $y -gt 9 ] do x=1 until [ $x -gt $y ] do echo -ne "$x*$y=$[ $x*$y ]\t" let x++ done echo echo let y++ done
case分支语句
1. 语法结构
语法:
case 变量名 in 模式1) 变量的值与模式1匹配时执行的操作;; 模式2) 变量的值与模式2匹配时执行的操作;; 模式N) 变量的值与模式N匹配时执行的操作;; *) 变量的值与以上所有模式都不匹配时执行的操作;; esac
或者:
case var in 定义变量;var代表是变量名 pattern 1) 模式1;用 | 分割多个模式,相当于or command1 需要执行的语句 ;; 两个分号代表命令结束 pattern 2) command2 ;; pattern 3) command3 ;; *) default,不满足以上模式,默认执行*)下面的语句 command4 ;; esac esac表示case语句结束
case通常和函数一起使用,用来给服务写启动脚本。
只接受参数start,stop,restart,status其中之一
#!/bin/bash # DEBUG=0 ADD=0 DEL=0 for I in `seq 0 $#`; do if [ $# -gt 0 ]; then case $1 in -v|--verbose) DEBUG=1 shift ;; -h|--help) echo "Usage: `basename $0` --add USER_LIST --del USER_LIST -v|--verbose -h|--help" exit 0 ;; --add) ADD=1 ADDUSERS=$2 shift 2 ;; --del) DEL=1 DELUSERS=$2 shift 2 ;; *) echo "Usage: `basename $0` --add USER_LIST --del USER_LIST -v|--verbose -h|--help" exit 7 ;; esac fi done if [ $ADD -eq 1 ]; then for USER in `echo $ADDUSERS | sed 's@,@ @g'`; do if id $USER &> /dev/null; then [ $DEBUG -eq 1 ] && echo "$USER exists." else useradd $USER [ $DEBUG -eq 1 ] && echo "Add user $USER finished." fi done fi if [ $DEL -eq 1 ]; then for USER in `echo $DELUSERS | sed 's@,@ @g'`; do if id $USER &> /dev/null; then userdel -r $USER [ $DEBUG -eq 1 ] && echo "Delete $USER finished." else [ $DEBUG -eq 1 ] && echo "$USER not exist." fi done fi
一个脚本showlogged.sh,其用法格式为:
showlogged.sh -v -c -h|--help
其中,-h选项只能单独使用,用于显示帮助信息;-c选项时,显示当前系统上登录的所有用户数;如果同时使用了-v选项,则既显示同时登录的用户数,又显示登录的用户的相关信息;如
Logged users: 4. They are: root tty2 Feb 18 02:41 root pts/1 Mar 8 08:36 (172.16.100.177) root pts/5 Mar 8 07:56 (172.16.100.177) hadoop pts/6 Mar 8 09:16 (172.16.100.177)
例子:
#!/bin/bash # declare -i SHOWNUM=0 declare -i SHOWUSERS=0 for I in `seq 1 $#`; do if [ $# -gt 0 ]; then case $1 in -h|--help) echo "Usage: `basename $0` -h|--help -c|--count -v|--verbose" exit 0 ;; -v|--verbose) let SHOWUSERS=1 shift ;; -c|--count) let SHOWNUM=1 shift ;; *) echo "Usage: `basename $0` -h|--help -c|--count -v|--verbose" exit 8 ;; esac fi done if [ $SHOWNUM -eq 1 ]; then echo "Logged users: `who | wc -l`." if [ $SHOWUSERS -eq 1 ]; then echo "They are:" who fi fi
2. 应用案例
㈠ 脚本传不同值做不同事
**具体需求:**当给程序传入start、stop、restart三个不同参数时分别执行相应命令
#!/bin/env bash case $1 in start|S) service apache start &>/dev/null && echo "apache 启动成功" ;; stop|T) service apache stop &>/dev/null && echo "apache 停止成功" ;; restart|R) service apache restart &>/dev/null && echo "apache 重启完毕" ;; *) echo "请输入要做的事情..." ;; esac
㈡ 根据用户需求选择做事
**具体需求:**
脚本提示让用户输入需要管理的服务名,然后提示用户需要对服务做什么操作,如启动,关闭等操作
#!/bin/env bash read -p "请输入你要管理的服务名称(vsftpd):" service case $service in vsftpd|ftp) read -p "请选择你需要做的事情(restart|stop):" action case $action in stop|S) service vsftpd stop &>/dev/null && echo "该$serivce服务已经停止成功" ;; start) service vsftpd start &>/dev/null && echo "该$serivce服务已经成功启动" ;; esac ;; httpd|apache) echo "apache hello world" ;; *) echo "请输入你要管理的服务名称(vsftpd)" ;; esac
㈢ 菜单提示让用户选择需要做的事
具体需求:
模拟一个多任务维护界面;当执行程序时先显示总菜单,然后进行选择后做相应维护监控操作
**具体需求:**
模拟一个多任务维护界面;当执行程序时先显示总菜单,然后进行选择后做相应维护监控操作
**********请选择*********
h 显示命令帮助
f 显示磁盘分区
d 显示磁盘挂载
m 查看内存使用
u 查看系统负载
q 退出程序
*************************
思路:
1. 菜单打印出来 2. 交互式让用户输入操作编号,然后做出相应处理
落地实现:
1. 菜单打印(分解动作)
#!/bin/env bash cat <<-EOF h 显示命令帮助 f 显示磁盘分区 d 显示磁盘挂载 m 查看内存使用 u 查看系统负载 q 退出程序 EOF
2. 最终实现
#!/bin/bash #打印菜单 cat <<-EOF h 显示命令帮助 f 显示磁盘分区 d 显示磁盘挂载 m 查看内存使用 u 查看系统负载 q 退出程序 EOF #让用户输入需要的操作 while true do read -p "请输入需要操作的选项[f|d]:" var1 case $var1 in h) cat <<-EOF h 显示命令帮助 f 显示磁盘分区 d 显示磁盘挂载 m 查看内存使用 u 查看系统负载 q 退出程序 EOF ;; f) fdisk -l ;; d) df -h ;; m) free -m ;; u) uptime ;; q) exit ;; esac done
或者:
#!/bin/bash #打印菜单 menu(){ cat <<-END h 显示命令帮助 f 显示磁盘分区 d 显示磁盘挂载 m 查看内存使用 u 查看系统负载 q 退出程序 END } menu while true do read -p "请输入你的操作[h for help]:" var1 case $var1 in h) menu ;; f) read -p "请输入你要查看的设备名字[/dev/sdb]:" var2 case $var2 in /dev/sda) fdisk -l /dev/sda ;; /dev/sdb) fdisk -l /dev/sdb ;; esac ;; d) lsblk ;; m) free -m ;; u) uptime ;; q) exit ;; esac done
课堂练习:
1. 输入一个等级(A-E),查看每个等级的成绩;如:输入A,则显示“90分~100分”,依次类推
2. 判断用户输入的字符串,如果是"hello",则显示"world";如果是"world",则显示"hello",否则提示"请输入hello或者world,谢谢!"
对性别进行判断,如果不输入男或者女,则显示”你是泰国来的吗?“
如果是男的,对其年龄进行判断。
2)
- 如果男的年龄大于等于18岁则显示“某某先生,你结婚了吗?”; - 如果对方回答结了或者yes,则显示“结了你来这凑什么热闹”; - 如果对方回答没有或者no,再次询问“那你有房有车吗?”; - 如果既不说结了也不说没结则显示:“你到底结没结婚啊?” - 如果回答有房有车,则显示”咱去民政局领证吧“; - 如果回答没有,则显示“不好意思,我去下洗手间。”; - 如果既不说有又不说没有,则显示“别浪费时间,请正面回答”。 - 如果男的年龄小于18岁,则显示“某某某你个小毛孩也来这凑热闹啦”
3)如果是女的,并且年龄大于等于18岁,则显示”某某女士你好“;否则显示”某某小姐你好“
参考: #!/bin/bash read -p "输入你的姓名:" name read -p "输入你的性别:" gender case "$gender" in 男|man|male|boy ) read -p "输入你的年龄:" age if [ $age -ge 18 ];then read -p "$name先生,你结婚了吗?" anwser case "$anwser" in 结了|有|yes ) echo "结了你还来干嘛?" ;; 没结|没有|没|no ) read -p "有房有车吗?" anwser2 case "$anwser2" in 有) echo "咱就直接去民政局领证吧" ;; 没有 ) echo "不好意思,我去下洗手间" ;; * ) echo "别浪费时间,请正面回答" esac ;; * ) echo "你到底结没结?" esac else echo "$name小子" fi ;; 女|woman|female|girl|lady ) read -p "输入你的年龄:" age if [ $age -ge 18 ];then echo "$name女士" else echo "$name小姐" fi ;; * ) echo "你是泰国来的吗?" esac 该程序有个bug:如果输入年龄为负数或者0也是可以的,如何修复bug?增加一个条件:如果输入的年龄小于等于10则显示:”不跟你玩了。。。“ 在最后加入如下语句即可: if [ $age -ge 18 ];then echo "$name女士" elif [ $age -le 10 ];then echo "不跟你玩了" exit 1 else echo "$name小姐" fi ;; * ) echo "你是泰国来的吗?" esac