[TOC]

0x07 shell编程选择分支

描述:我们学过的每一种高级编程语言,都在存在流程/分支选择/循环等结构,同样shell编程中也有这是与windows上的bat编程不一样的点;shell选择分支嵌套建议不超过三层,为了执行的效率和其他人容易读懂;

if 语句

if 语句通过关系运算符判断表达式的真假来决定执行哪个分支,Shell中有三种 if … else 语句表示结构:

1
2
3
4
5
6
#1.单分支语句
if ...;then ... fi 语句;
if ...;then ... else ... fi 语句;

#2.双分支语句
if ...;then ... elif ...;then ... else ... fi 语句

(1)单分支语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#1.语法结构 if......fi语句
#如果 expression 返回 true,then 后边的语句将会被执行;如果返回 false,不会执行任何语句。
if [ expression ]
then
Statement(s) to be executed if expression is true
fi #最后必须以 fi 来结尾闭合 if,fi 就是 if 倒过来拼写,后面也会遇见。

#即
if [表达式];then
语句
fi


#2.语法结构 if ... else ... fi 语句
# 如果 expression 返回 true,那么 then 后边的语句将会被执行;否则,执行 else 后边的语句
if [ expression ]
then
Statement(s) to be executed if expression is true
else
Statement(s) to be executed if expression is not true
fi

WeiyiGeek.if..fi

WeiyiGeek.if..fi

WeiyiGeek.if..else..fi

WeiyiGeek.if..else..fi


(3)多分支语句

1
2
3
4
5
6
7
8
9
10
11
12
13
#3.语法结构  if ... elif ...else... fi 语句
#可以对多个条件进行判断,注意的是每个条件表达式后面都有一个then,语法为:
#依次判断,为真则结束判断,为Fasle则继续向下判断.
if [ expression 1 ];then
Statement(s) to be executed if expression 1 is true
elif [ expression 2 ];then
Statement(s) to be executed if expression 2 is true
elif [ expression 3 ];then
Statement(s) to be executed if expression 3 is true
else
Statement(s) to be executed if no expression is true
fi
#哪一个 expression 的值为 true,就执行哪个 expression 后面的语句;如果都为 false,那么不执行任何语句

WeiyiGeek.if..elif

WeiyiGeek.if..elif

实际案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/usr/bin/env bash
read -p "Please INput A:" a
read -p "Please INput B:" b
read -p "Please INput C:" c

echo -e "\n\e[1;32m ##示例1.单分支语句 \e[0m"
#注意:条件表达式要放在方括号之间,并且要有空格,例如 [$a==$b] 是错误的,必须写成 [ $a == $b ]
if [ $a == $b ];then #注意[ 里面的关系运算符要用空格隔开 ]
echo "a is equal to b!!"
fi
if [ $a != $b ];then
echo "a is not equal to b!!"
fi
#if语句也可以写成一行,以命令的方式来运行,像这样:
if test $[2*3] -eq $[1+5]; then echo 'The two numbers are equal!'; fi;


echo -e "\n##示例2.单分支语句"
#判断从标准输入的变量b和c的值是否相等
if [ $c == $b ];then
echo "a is equal to b!!"
else
echo "a is not equal to b!!"
fi

#判断iptables是否在运行,如果已经在运行提示信息,如果没有开启它。
service iptables status &> /dev/null
if [ $? -eq 0 ];
then
echo "iptables service is running"
else
service iptables restart
fi


echo -e "\n##示例3.多分支语句"
#注意elif使用中没一个条件后都有一个then关键字;
a=10;b=20
if [ $a == $b ];then
echo "a is equal to b"
elif [ $a -gt $b ];then # >
echo "a is greater than b"
elif [ $a -lt $b ];then # <
echo "a is less than b"
else
echo "None of the condition met"
fi

WeiyiGeek.if示例

WeiyiGeek.if示例

if…else 语句也经常与 test 命令或者 [ ] 结合使用如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/bin/env bash
#if与test命令和[]以及[[]]联合使用

#示例1.test
num1=$[2*3];num2=$[1+5]
if test $[num1] -eq $[num2]
then
echo 'The two numbers are equal!'
else
echo 'The two numbers are not equal!'
fi
#The two numbers are equal!


#示例2.[]
read -p "测试输入:" number
if [ ! -z "$number" -a "$number" == "1024" ];then #注意这里变量外面的双引号(非常重要)
echo "输出正确:1024"
else
echo "您不满足条件!"
fi
#输出正确:1024


#示例3.[[]]
a=$(env|grep "USER"|cut -d "=" -f2)
if [[ $a == "root" ]];then
echo "Current User is Root"
elif [[ ! -z $a ]];then
echo "Current User is ${a}"
fi
#Current User is ubuntu

高级示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/bi/bash
#示例1.判断分区得使用率,建议if中表达式使用括号围起来
rate=$(df -h | grep "rootfs" | awk '{print $5}' | cut -d "%" -f1)
if (test $rate -ge 10 );then
echo "Warning /dev/sda1 is full ${rate}"
else
echo "/dev/sda1 is more free${rate}"
fi
#Warning /dev/sda1 is full 19


#示例2.判断一个是不是一个目录,变量必须采用""进行包含
read -t 30 -p "请输入一个文件路径或者目录路径:" fdpath
if (test -f "$fdpath");then
echo "它是一个文件,路径是:$fdpath"
elif (test -d "$fdpath");then
echo "它是一个目录,路径是:$fdpath"
else
echo "既不是目录也不是文件!"
fi
#它是一个文件,路径是:/tmp/test.txt


#示例3.判断apache服务得shell脚本名字不能有httpd关键字
test=$(ps aux | grep httpd | grep -v httpd) #截取http进程并且排除该项命令
if [[ -n "$test" ]];then
#在不为空的情况下执行 -z : 是不是为零(是则真)
echo "$(date) httpd is ok!" >> /tmp/httpslogs.logs
else
#否则重新启动httpd
/etc/rc.d/init.d/httpd stop &> /dev/null
/etc/rc.d/init.d/httpd start &> /dev/null
echo "$(date) restart httpd !!" >> /tmp/httpderror.logs
fi

WeiyiGeek.if常规运算

WeiyiGeek.if常规运算


case 语句

描述:case … esac 与其他语言中的 switch … case 语句类似,也是一种多分支选择结构。
case 语句匹配一个值或一个模式,如果匹配成功,执行相匹配的命令。

语句格式语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
case var值 in
"值1")
command1 #如果等于值1则执行
command2
;;
"值2")
command1
command2
;;
*)
command1 #如果变量值都不是以上值,则执行此程序;
command2
;;
esac
```
case工作方式如上所示:取值后面必须为关键字 in,每一模式必须以右括号结束。取值可以为`变量或常数`,匹配发现取值符合某一模式后,其间所有命令`开始执行直至 ;;` ,取值将检测匹配的每一个模式:一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。
- 如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令
- ;; 与其他语言中的 break 类似,意思是跳到整个 case 语句的最后

![WeiyiGeek.case语句](https://cdn.jsdelivr.net/gh/WeiyiGeek/blogimage/2019/20190708170556.png)

实际案例:
```bash
#!/bin/bash
#示例1. 下面的脚本提示输入1到4,与每一种模式进行匹配
echo 'Input a number between 1 to 4'
echo 'Your number is:\c'
read aNum
case $aNum in
1) echo 'You select 1'
;;
2) echo 'You select 2'
;;
3) echo 'You select 3'
;;
4) echo 'You select 4'
;;
*) echo 'You do not select a number between 1 to 4'
;;
esac


#示例2:比较新颖的思路
option="${1}"
case ${option} in
-f|-F)
FILE="${2}"
echo "File name is $FILE"
;;
-d|-D)
DIR="${2}"
echo "Dir name is $DIR"
;;
#还可以这样
[yY]|[yY][eE][sS])
echo "YES"
;;
[nN]|[nN][oO]
echov "NO"
;;
*)
echo "`basename ${0}`:usage: [-f file] | [-d directory]"
exit 1 # Command to come out of the program with status 1
;;
esac

WeiyiGeek.case语句示例

WeiyiGeek.case语句示例

注意事项:

  • expression 和方括号([ ])之间必须有空格,否则会有语法错误
  • 可以使用三种方式让while循环读文件,但是并不建议采用while而是通过awk可以更快;

0x08 shell编程循环退出

for 语句

描述:与其他编程语言类似,Shell支持for循环列表是一组值(数字、字符串等)组成的序列,每个值通过空格分隔,每循环一次,就将列表中的下一个值赋给变量。

for语句语法格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
##方法1:##
#in 列表是可选的,如果不用它 for 循环使用命令行的位置参数
for 变量 in 取值列表;do
command1
command2
...
commandN
done

##方法2##
for((初始值;循环条件;变量控制))
do
commond
done

WeiyiGeek.for语句

WeiyiGeek.for语句

for循环中用seq产生循环次数,加上C语言形式的for循环语句;
对于固定次数的循环,可以通过seq命令来实现,就不需要变量的自增了,这里的C语言for循环风格是挺熟悉的吧。

实际案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#!/bin/bash
#示例1.循环示例
for loop in 1 2 3;do
echo "(1)The value is: $loop"
done

for loop in $(seq 0 2);do
echo "(2)The value is: $loop"
done

for i in {0..2} #输出{1..2}列表里面的数字,等同于seq 1 2
do
echo $i
done

#执行结果:
(1)The value is: 1
(1)The value is: 2
(1)The value is: 3
(2)The value is: 0
(2)The value is: 1
(2)The value is: 2


#示例2.对目录中的文件做for循环
#如果只引用当前工作目录中的文件(例如如果输入for x in *),则产生的文件列表将没有路径信息的前缀(可以不用basename)
for x in /var/log/*
do
#这个$x获得的是绝对路径文件名,可以使用basename可执行程序来除去前面的路径信息
echo $(basename $x) is a file living in /var/log
done

# 执行结果:
# alternatives.log is a file living in /var/log
# apt is a file living in /var/log
# btmp is a file living in /var/log

# 批量压缩有特点格式的日志文件:
for i in $(ls info-20210427.log.{0..5});do tar -zcvf ${i}.tar.gz ${i};done
rm -rf info-20210427.log.{0..5}

# 示例3.批量解压包
ls *.tar.gz >> gz.log 2>/dev/null
ls *.tgz >> gz.log 2>/dev/null
echo "正在解压缩 : Wait,Please!!"
for i in $(cat gz.log) #【这里值得学习在写脚本的时候非常有用】
do
tar -zxf $i &> /dev/null
done
rm -rf gz.log


# 示例4.利用计算1到100相加
sum=0
for((i=0;i<=100;i++))
do
sum=$[ $sum + $i ]
done
echo "一到一百相加得:$sum" #5050


# 示例5.快速时间同步
for i in {78..83};do echo -n "$i -";ssh -p 20211 [email protected]${i} 'ntpdate 192.168.10.254 && date';done

高级shell编程:
passwd默认是要用终端作为标准输入,加上–stdin表示可以用任意文件做标准输入
于是这里用管道作为标准输入,且直接传人密码,不用重复输入.

1
2
3
4
5
# 批量建立用户
echo [email protected]@56wn | /usr/bin/passwd test
echo [email protected]@56wn | /usr/bin/passwd --stdin test
# --stdin 将echo输出得值接收赋予 test 用户
--stdin 从标准输入读取令牌(只有根用户才能进行此操作)

WeiyiGeek.建立用户

WeiyiGeek.建立用户

同样我们可以批量删除用户

1
2
3
4
5
6
# less /etc/passwd | grep "/bin/bash" | grep -v root | cut -d ":" -f1 
Srcweb
postgres
mysql
git
test

WeiyiGeek.删除用户

WeiyiGeek.删除用户


while 语句

描述:while 循环用于不断执行一系列命令(为真执行),也用于从输入文件中读取数据;命令通常为测试条件。

基础语法:

1
2
3
4
5
#命令执行完毕,控制返回循环顶部,从头开始直至测试条件为假
while [ 条件表达式] #条件成立会一直执行
do
Statement(s) to be executed if command is true
done

WeiyiGeek.while语句

WeiyiGeek.while语句

实际案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#示例1.基本的while循环,测试条件是:
#如果COUNTER小于5,那么返回 true,COUNTER从0开始,每次循环处理时,COUNTER加1。运行上述脚本返回数字1到5然后终止
COUNTER=0
while [[ $COUNTER -lt 5 ]]
do
echo $((COUNTER++)) > /dev/null # 相当于在循环内自加-在i++
echo -n "$COUNTER "
done
#执行结果
1 2 3 4 5


#示例2.从1加到100的while计算方式.
i=1;s=0
while [[ $i -le 100 ]];do
s=$(( $s+$i )) #变量赋值不用带$+变量
i=$(( $i+1 ))
done
echo "1+2+...+10 sum is: ${s}" #1+2+...+10 sum is: 5050


#示例3.while循环可用于读取键盘信息,思路毕竟新新颖.
#下面的例子中,输入信息被设置为变量FILM,按<Ctrl-D>结束循环
echo 'type <CTRL-D> to terminate'
echo -n 'enter your most liked film: '
while read FILM
do
echo "Yeah! great film the $FILM"
done

WeiyiGeek.while循环监听输入

WeiyiGeek.while循环监听输入


until 语句

描述:until 循环用于不断执行一系列命令(为真停止),也用于从输入文件中读取数据;命令通常为测试条件。
Tips:一般while循环优于until循环,但在某些时候,也只是极少数情况下,until 循环更加有用。

  • until 循环执行一系列命令直至条件为 true 时停止。
  • until 循环与 while 循环在处理方式上刚好相反。

基础语法:

1
2
3
4
5
# command 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环
until [ 表达式 ]
do
Statement(s) to be executed until command is true
done

实际案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 例1:使用 until 命令输出 0 ~ 9 的数字: 
#!/bin/bash
a=0
until [ ! $a -lt 10 ] # a小于10取反->a大于10为假,则执行
do
echo $a
a=`expr $a + 1`
done


#示例2. 从1加到100的until计算方式.
i=1;s=0
until [[ $i -gt 100 ]];do
s=$(( $s+$i ))
i=$(( $i+1 ))
done
echo "The sum is: ${s}" #The sum is: 5050


在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,像大多数编程语言一样,Shell也使用 break 和 continue 来跳出循环。

break语句

描述:在for、while、until等循环语句中,用于跳出当前所在的循环体,执行循环体之后的语句。

基础语法:

1
2
3
4
5
6
7
8
9
10
11
12
#示例1:
while [[ express ]];do
commands
#通常在循环体中与条件语句一起使用
if [ express ];then
break;
fi
done

#示例2. 在嵌套循环中,break 命令后面还可以跟一个整数,表示跳出第几层循环。
#例如: 表示跳出第 n 层循环
break n

实际案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#示例1.脚本进入死循环直至用户输入数字大于5要跳出这个循环,返回到shell提示符下,就要使用break命令。 
while : #死循环 (注意这里的 : 也是一条命令,无论怎么样返回都为真且exit状态为0)
do
echo -n "Input a number between 1 to 5: "
read aNum
case $aNum in
1|2|3|4|5) echo "Your number is $aNum!"
;;
*) echo "You do not select a number between 1 to 5, game is over!"
break
;;
esac
done


#示例2.嵌套循环的例子,如果 var1 等于 2,并且 var2 等于 0,就跳出2层循环:
for var1 in 1 2 3
do
for var2 in 0 5
do
if [ $var1 -eq 2 -a $var2 -eq 0 ] #当var1=2 与 var=0时跳出循环
then
break 2
else
echo "$var1 $var2"
fi
done
done

#运行结果
1 0
1 5

WeiyiGeek.示例1

WeiyiGeek.示例1


continue 语句

描述:在for、while、until等循环语句中,用于跳过循环体内余下的语句,重新判断条件以便执行下一次循环; 同样continue 后面也可以跟一个数字,表示跳出第几层循环继续下一次循环。

语法示例:

1
2
3
4
5
6
7
8
9
while :
do
commands
if [[ express ]];then
#跳出当前循环,重新进入下一次循环(与break一样通常与条件语句一起使用)
continue
fi
commands
done

实际案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#示例1.当输入的数字是1~5则退出while循环,否则继续执行 *) 中的命令并且永远也不会输出 Game is over
while :
do
echo -n "Input a number between 1 to 5: "
read aNum
case $aNum in
1|2|3|4|5) echo "Your number is $aNum!"
break
;;
*) echo "You do not select a number between 1 to 5!"
continue
echo "Game is over!"
;;
esac
done

运行代码发现,当输入大于5的数字时,该例中的循环不会结束,语句echo “Game is over!” # 永远不会被执行