shell编程流程控制
前言:
在linux shell中,通常我们将一些命令写在一个文件中就算是一个shell脚本了,但是如果需要执行更为复杂的逻辑判断,我们就需要使用流程控制语句来支持了。
所谓流程控制既是通过使用流程控制语句对程序流程的选择、循环、转向和返回等进行控制。流程控制是所有编程语言分重要组成部分,linux shell同样有一套自己的流程控制语句,其中主要包括条件语句(if),循环语句(for,while),选择语句(case)。本文将会对这几种语句进行介绍同时引用示例便于大家理解。文章也会涉及到循环控制语句的介绍,以及while循环的一些常用的特殊用法,快来一起学习吧!
一、条件选择if语句
单分支
if 判断条件;then
条件为真的分支代码
fi
双分支
if 判断条件; then
条件为真的分支代码
else
条件为假的分支代码
fi
多分支
if 判断条件1; then
条件为真的分支代码
elif 判断条件2; then
条件为真的分支代码
elif 判断条件3; then
条件为真的分支代码
else
以上条件都为假的分支代码
fi
逐条件进行判断,第一次遇为“真”条件时,执行其分支, 而后结束整个if语句
二、条件判断case语句:
case 变量引用 in #变量的引用加$
PAT1)
分支1
;;
PAT2)
分支2
;;
...
\*)
默认分支
;;
esac
case支持glob风格的通配符:
*: 任意长度任意字符
?: 任意单个字符
[]:指定范围内的任意单个字符
a|b: a或b
练习:编写脚本/root/bin/createuser.sh,实现如下功能:使用一个用户名做为参数,如果指定参数的用户存在,就显示其存在,否则添加之;显示添加的用户的id号等信息
[root@CentOS6 bin]#vim iduser.sh
1 #!/bin/bash
2 #---------------+--------------------------------------+
3 # ☆ Author : huxiaoqi
4 # ☆ Email : 345304057@qq.com
5 # ☆ Blog : http://www.cnblogs.com/qige2017/
6 # ☆ Tel : 13598859921
7 # ☆ Create time : 2017-08-28
8 # ☆ Filename : iduser.sh
9 # ☆ Version : 1.2.1
10 #---------------+--------------------------------------+
11 read -p "Please input the user name: " username #交互式输入用户名,复制给username
12 if id $username &>/dev/null ;then #条件判断用户输入的用户名是否存在
13 echo "$username is exist" #用户名已存在满足条件,打印exist
14 else
15 echo "user$username is cresting.."
16 sleep 1
17 useradd $username &> /dev/null #用户名不存在就创建用户
18 finger $username #查看用户相关信息
19 fi
三、for循环:
for 变量名 in 列表;do
循环体
done
[root@CentOS7 ~]#for id in {10..1..2};do echo The number is $id ;done
The number is 10 #列表也可以`seq 2 2 10`
The number is 8 #同样表示2到10步进2
The number is 6 #因为是命令所以用反引号引起来
The number is 4
The number is 2
执行机制:
依次将列表中的元素赋值给“变量名”; 每次赋值后即执 行一次循环体; 直到列表中的元素耗尽,循环结束
列表生成方式:
- 直接给出列表
- 整数列表:
(a) {start..end}
(b) $(seq [start [step]] end) - 返回列表的命令
$(COMMAND) - 使用glob,如:*.sh
- 变量引用; $@, $*
练习:用for实现
1、添加10个用户user1-user10,密码为8位随机字符
[root@CentOS6 bin]#vim useradd10.sh
#!/bin/bash
> /root/user.log #创建并每次清空密码记录文件
for i in {1..10};do #1-10 循环10次
if id user$i &> /dev/null ;then #判断用户是否存在
echo user$i is exist
exit 1 #若存在则退出脚本,有漏洞可以用后边的知识跳过本次循环
fi
useradd user$i &> /dev/null && echo user$i is created
passwd=`cat /dev/urandom | tr -dc 'a-cA-Z0-9' |head -c 8`
#取/dev/urandom文件中的随机字符,tr -dc 'a-cA-Z0-9' |head -c 8 过滤删 除除了大小写字母以及数字的字符,并取前八个(cat /dev/urandom每次生成的随机 数都不一样)。并且赋值passwd变量。
echo $passwd | passwd --stdin user$i &> /dev/dull
passwd -e user$i &> /dev/null
#将用户的密码修改为$passwd,并设置强制登录修改密码
echo user$i:$passwd >> /root/user.log
#将用户名和对应的用户密码重定向到/root/user.log中
done
[root@CentOS6 ~]#cat user.log
user1:THUU66I0
批量创建用户
[root@CentOS6 ~]#echo user{1..10} |xargs -n1 useradd
批量删除用户
[root@CentOS6 ~]#echo user{1..10} |xargs -n1 userdel -r
2、计算100以内所有能被3整除的整数之和
步进思路:
[root@CentOS6 bin]#vim be3.sh
#!/bin/bash
sum=0 #进入循环之前总合为0
for i in `seq 3 3 100`;do #以3开始步进3到100,既取出所有3-100被三整除的数
sum=$[i+sum] #进入循环以后,sum自增方括号内是数值运算不用加空格
其他写法:let sum+=i 或者sum=$((i+$sum)) 小括号方括号内均为数值运算$可以省略
done
echo $sum
取模思路:
sum1=0
for ii in `seq 1 100`;do
if [ $((ii%3)) -eq 0 ];then #若返回值为0则继续执行任务
#计算机只支持整数运算,若ii的值不能被3整除则返回值为假,既不等于0
sum1=$[ii+sum1]
fi
done
echo sum1 is: $sum1
tr思路:
echo {3..100..3}|tr ' ' +|bc
seq思路:
seq -s + 3 3 100 |bc #-s:指定分隔符为+号
3、打印九九乘法表
for l in `seq 9`;do #l:行数循环9次
for q in `seq $l`;do #q列数嵌套循环,每行的列数=当前行数
p=$[q*l] #p为乘积
echo -n "$q*$l=$p " #内层循环,也就是每一行内的循环echo不换行
done
echo #外层循环要换行
done
[root@CentOS6 bin]#99.sh
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
4、打印等腰三角形
[root@CentOS6 bin]#vim triangle.sh
#!/bin/bash
read -p "please input the storey of the triangle: " n
for l in `seq $n` ; do #命令的引用可以用反引号或者$()
for s in `seq $((n-l))`; do #空格数=总行数减当前行数,bash中的算术运算:var=$((算术表达式))或者 var=$[算术表达式]
echo -n " "
done
for t in `seq $((2*l-1))`;do #星数=当前行数*2-1
echo -n "*"
done
echo #每次最外层循环后换行
done
[root@CentOS6 bin]#triangle.sh
please input the storey of the triangle: 5
*
***
*****
*******
*********
四、while循环
用法展示:
while CONDITION; do
循环体
done
CONDITION:循环控制条件;进入循环之前,先做一次判 断;每一次循环之后会再次做判断;条件为“true”,则执行 一次循环;直到条件测试状态为“false”终止循环 。
因此:CONDTION一般应该有循环控制变量;而此变量的值 会在循环体不断地被修正。
进入条件:CONDITION为true
退出条件:CONDITION为false
练习:用while循环实现
1、编写脚本,提示请输入网络地址,如192.168.0.0,判断 输入的网段中主机在线状态,并统计在线和离线主机各多少
[root@CentOS6 bin]#vim whileip.sh
#!/bin/bash
read -p "please input the ip(eg:172.18.0.1): " ip
network=`echo $ip |cut -d "." -f1-3` #C类地址前三位为网络id
i=1 #ip一般从1开始
up=0
down=0
while [ $i -le 254 ];do #255作为网段内的广播ip,所以取1-254之内的循环
if ping -c1 -w1 $network.$i > /dev/null;then #ping一次一秒
echo $network.$i is up!
let up++ #统计开机主机数
else
echo $network.$i is down!
let down++ #统计关机主机数
fi
let i++
done
2、编写脚本,实现打印国际象棋棋盘
思路:
- 棋盘分八行八列,外层循环八次表示行数,内层循环八次表示列数
- 打印空格加底色实现棋盘效果
- 总结规律发现当当前行数与当前列数相加为偶数时打印黄色,当当前行数与当前列数相加为奇数时打印红色
[root@CentOS6 bin]#vim chess.sh
#!/bin/bash
i=1 #初始行数为1进入循环
while [ $i -le 8 ];do #行数小于等于8则继续任务
j=1 #初始列数为1进入内层循环
while [ $j -le 8 ];do #列数小于等于8册继续任务
flag=$[$[i+j]%2] #标记当前行数当前列数和的奇偶性
if [ $flag -eq 0 ];then #当$flag为偶数打印黄色
echo -n -e "\033[43m \033[0m"
else #当$flag为奇数数打印红色
echo -n -e "\033[41m \033[0m"
fi
let j++ #列数自增
done
let i++ #行数自增
echo
done
while的无限循环写法
冒号:
ture
两个反引号``
作为while的循环控制条件,返回值永远为真,可以无限循环。
[root@CentOS7 ~]#while true ; do echo hello ; sleep 1 ; done
hello
hello
let 自增赋值坑
之前学习bash算法时有关于自增赋值先后区别的描述,这一区别在while循环用法中得以体现:
[root@CentOS6 bin]#i=0;let i=++i;echo $?
0 #当初始值为0时使用let i=++i 先自增再赋值返回值为真
[root@CentOS6 bin]#i=0;let i=i++;echo $?
1 #当初始值为0时使用let i=++i 先赋值再自增返回值为假
五、until循环
用法展示:
until CONDITION; do
循环体
done
原理与while相反:
进入条件: CONDITION 为false
退出条件: CONDITION 为true
练习until:
每隔3秒钟到系统上获取已经登录的用户的信息;如果发 现用户tianbaobao登录,则将登录时间和主机记录于日志 /app/login.log中,并退出脚本
[root@CentOS6 bin]#vim untilwho.sh
#!/bin/bash
until who | grep "^tianbaobao\>" &>> /app/login.log ; do
#用grep过滤who命令以tianbaobao开头的输出结果作为until的条件判断
sleep 3 #每隔三秒
done
六、循环控制语句continue
在循环任务中,如果遇到continue就跳过本次循环,执行下一次循环
continue后边加参数continue [N]:指定提前结束第N层的本轮循环,而直接进入下一 轮判断;最内层为第1层
while CONDTIITON1; do
CMD1
...
if CONDITION2; then
continue #如果满足条件2执行continue
fi
CMDn #跳过接下来的任务,但是继续执行循环任务
...
done
七、循环控制语句break
在循环任务中,如果遇到break就结束整个循环任务。
break后边加参数break [N]:指定提前结束第N层循环,最内层为第1层
while CONDTIITON1; do
CMD1
...
if CONDITION2; then
break ##如果满足条件2,执行break直接结束整个循环任务
fi
CMDn
...
done
八、循环控制shift命令
shift [n]
用于将参量列表 list 左移指定次数,默认为左移一次。
参量列表 list 一旦被移动,最左端的那个参数就从列表中删 除。
while 循环遍历位置参量列表时,常用到 shift
示例:
while [ $# -gt 0 ] # or (( $# > 0 ))
do
echo $*
shift
done
练习shift:
利用shift实现批量创建用户
[root@CentOS6 bin]#vim useraddshift.sh
#!/bin/bash
while [ $# -gt 0 ] ;do #如果参数大于零进入循环
id $1 &> /dev/null && continue #如果用户已存在就跳过本次循环
useradd $1 && echo $1 is created
shift #移除$1,$2左移成为下次循环的$2
done
九、特殊用法
while循环的特殊用法(遍历文件的每一行):
while read line; do
循环体
done < /PATH/FROM/SOMEFILE
依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将 行赋值给变量line
双小括号写法
双小括号方法,即((…))格式,也可以用于算术运算
双小括号方法也可以使bash Shell实现C语言风格的变量操作
I=10
((I++))
for循环的特殊格式:
for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))
do
循环体
done
控制变量初始化:仅在运行到循环代码段时执行一次
控制变量的修正表达式:每轮循环结束会先进行控制变量修 正运算,而后再做条件判断
练习特殊用法:
计算1-100所有数字之和
[root@CentOS6 bin]#vim 1-100sum.sh
1 #!/bin/bash
2 #---------------+--------------------------------------+
3 # ☆ Author : huxiaoqi
4 # ☆ Email : 345304057@qq.com
5 # ☆ Blog : http://www.cnblogs.com/qige2017/
6 # ☆ Tel : 13598859921
7 # ☆ Create time : 2017-08-26
8 # ☆ Filename : 1-100sum.sh
9 # ☆ Version : 1.2.1
10 #---------------+--------------------------------------+
11 i=1 #初始化
12 sum=0
13 while [ $i -le 100 ];do #i小于等于100既进入循环
14 let sum+=i #求和
15 let i++ #i自增
16 done
17 echo usm is: $sum
18 i=1 sum=0
19 while [ $i -le 100 ];do
20 sum=$[i+sum]
21 let i++
22 done
23 echo sum1 is: $sum
24 for ((sum=0,i=0;i<=100;i++))#利用双小括号写法
#sum,i=0控制变量初始化;i<=100条件判断表达式;i++控制变量修正表达式
25 do
26 let sum+=i
27 done
28 echo sum2 is: $sum
29 i=1
30 sum=0
31 while ((i<=100))
32 do
33 let sum+=i
34 let i++
35 done
36 echo sum3 is: $sum
37 unset i sum
四种方法
[root@CentOS6 bin]#1-100sum.sh
usm is: 5050
sum1 is: 5050
sum2 is: 5050
sum3 is: 5050
这个脚本写法比较正规,题头比较完整,变量的初始化以及sunset都有,逻辑清晰有条理,排版合理大家可以参照养成良好的编写脚本的习惯。
十、select循环与菜单
select variable in list #variable:变量名
do
循环体命令
done
select 循环主要用于创建菜单,按数字顺序排列的 菜单项将显示在标准错误上,并显示 PS3 提示符, 等待用户输入
(PS1:命令行提示符;PS2:多行重定向提示符)
用户输入菜单列表中的某个数字,执行相应的命令
用户输入被保存在内置变量REPLY中
select 是个无限循环,因此要记住用 break 命令退 出循环,或用 exit 命令终止脚本。也可以按 ctrl+c 退出循环
select 经常和 case 联合使用
与 for 循环类似,可以省略 in list,此时使用位置参量
练习select:
制作菜单,要求顾客输入对应食物的编号就显示价格
[root@CentOS6 bin]#vim selectmenu.sh
PS3="Please choose your food: "
select menu in lamian huimian hulatang yangroutang ;do
case $REPLY in
1)
echo "The price is \$10"
;;
2)
echo "The price is \$15"
;;
3)
echo "The price is \$5"
;;
4)
echo "The price is \$20"
;;
*)
echo "No such food"
break #select语句是一个死循环与break配合使用
;;
esac
done
[root@CentOS6 bin]#selectmenu.sh
1) lamian #selsct语句自动列出菜单,并且定义用户需要输入的编号
2) huimian #将用户输入的编号赋值给REPLY
3) hulatang
4) yangroutang
Please choose your food: 1
The price is $10
Please choose your food: 2
The price is $15
Please choose your food: 5
No such food
关于linux shell流程控制的知识就介绍到这儿啦,怎么样,看完是不是觉得文章真的是又简单易懂又实用呢?希望以上的内容能给你带来帮助,有疑问以及要指正的错误,请留言吧!