大家好呀!今天简单聊一聊Shell的流程控制。任何一门编程语言的学习,在了解了其基本的语法规则和变量外,都是从基础的流程控制语句开始学习。流程控制无非是顺序、分支(选择)加上循环,对于任何一门编程语言而言都是类似的。

一、选择语句

1、语法结构

1)if语句

只有条件为真才会执行command命令

1
2
3
4
5
6
if [ condition ];then
command
command
fi

[ 条件 ] && command

2)if-else语句

只有条件为真才会执行command1,否则执行command2

1
2
3
4
5
if [ condition ];then
command1
else
command2
fi

或使用条件判断(逻辑与、逻辑或)

1
[ 条件 ] && command1 || command2

&&:前面的表达式为真,才会执行后面的内容
||:前面的表达式为假,才会执行后面的内容

3)if-elif-else语句

如果条件1满足,执行命令1后结束;如果条件1不满足,再看条件2,如果条件2满足执行命令2后结束;如果条件1和条件2都不满足执行命令3结束。

1
2
3
4
5
6
7
if [ condition1 ];then
command1
elif [ condition2 ];then
command2
else
command3
fi

4)嵌套if

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if [ condition1 ];then
command1
if [ condition2 ];then
command2
fi
else
if [ condition3 ];then
command3
elif [ condition4 ];then
command4
else
command5
fi
fi

2、案例

判断当前主机和远程主机是否ping通

1)创建ping.sh脚本

1
vim ping.sh

2)根据要求编写脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash

# Name: ping.sh
# Path: /shell02/ping.sh
# Usage: /shell02/ping.sh
# Update: 2024-08-08 21:56
# ...

#获取远程主机的IP地址(定义变量让用户自己输入)
read -p "请输入你要ping的远程主机IP:" IP
#使用ping命令来判断是否和远程主机互通
ping -c1 $IP &>/dev/null
if [ $? -eq 0 ];then
echo "当前主机和远程主机$IP是互通的。"
else
echo "当前主机和远程主机$IP是不通的。"
fi

-c:次数,ping -c1表示ping 1次

注:/dev/null 属于字符特殊文件,它属于空设备,是一个特殊的设备文件,它会丢弃一切写入其中的数据,写入它的内容都会永远丢失。此处将ping的结果重定向到空设备文件,即不显示ping结果的具体信息

优化:在执行脚本时直接让用户传入一个参数($1)

1
2
ping -c1 $1 &>/dev/null
[ $? -eq 0 ] && echo "当前主机和远程主机$1是互通的。" || echo "当前主机和远程主机$1是不通的。"

在执行脚本时可以让用户传入多个参数($#)

1
2
3
4
5
6
7
8
if [ $# -ne 1 ];then
echo "Usage:$0 remote_ip" && exit
fi
# 将上述if语句写成1行条件判断
[ $# -ne 1 ] && echo "Usage:$0 remote_ip" && exit

ping -c1 $1 &>/dev/null
[ $? -eq 0 ] && echo "当前主机和远程主机$1是互通的。" || echo "当前主机和远程主机$1是不通的。"


exit:退出,直接退出程序
$0:脚本本身的名字
$#:脚本后面接的参数的个数
$1~$9 位置参数变量

二、循环语句

1、for循环

1.1 列表循环

列表for循环:用于将一组命令执行已知的次数
注:循环次数由列表决定。

1)语法结构

1
2
3
4
5
6
for variable in {list}
do
command
command

done

1
2
3
4
5
for variable in a b c
do
command
command
done

2)eg:

输出1-5

1
2
3
4
5
6
7
8
#方式1
for i in {1..5};do echo $i;done
#方式2
for i in `seq 5`;do echo $i;done
#方式3
for i in $(seq 5);do echo $i;done
#方式4
for i in 1 2 3 4 5;do echo $i;done

输出1-10的奇数

1
2
for i in {1..10..2};do echo $i;done
# 1 3 5 7 9

输出1-10的偶数(逆序)

1
2
3
4
# 10 8 6 4 2
for i in {10..1..-2};do echo $i;done
# 或
for i in {10..1..2};do echo $i;done

用反引号,将shell命令引起来,可以将命令的输出值赋给变量。

反撇号``:反撇号和$()一样,引号或括号里的命令会优先执行,如果存在嵌套,使用$(),反撇号不能用

1.2 不带列表循环

循环次数由用户或脚本后面参数个数决定

1)语法结构

1
2
3
4
5
6
for variable
do
command
command

done

2)eg:

编写一个shell脚本,实现不带列表循环。

1
2
3
4
5
6
7
8
9
vim test.sh

#!/bin/bash
for i
do
echo $var
done

echo "脚本后面有$#个参数"

1)do...done为循环体,do开始循环,done结束循环

2)非列表循环,变量取值由用户决定。脚本后面传一个参数1,就会把1传给参数i,就会打印出来1

3)使用bash -x查看脚本的执行过程。

1.3 类C风格

1)语法结构

1
2
3
4
5
6
for(( expr1;expr2;expr3 ))
do
command
command

done

expr1:定义变量并赋初值
expr2:循环的终止条件
expr3:循环变量的改变方式

2)案例

eg1

1
2
3
4
5
6
7
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


注:类C风格循环次数由循环终止条件和循环变量变化方式决定。

eg2:计算1到100的奇数和

1)创建test1.sh脚本

1
vim test1.sh

2)根据要求编写脚本

1
2
3
4
5
6
7
8
#!/bin/bash
sum=0
for i in {1..100..2}
do
sum=$[ $i + $sum ]
done
#打印1-100的奇数和
echo "1-100的奇数和为:$sum"

1
2
3
4
5
6
7
#!/bin/bash
sum=0
for (( i=1;i<=100;i+=2))
do
let sum=sum+$i
done
echo "1-100的奇数和为:$sum"

1
2
3
4
5
6
7
8
9
#!/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"

1
2
3
4
5
6
7
#!/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

2、循环控制

循环体: do…done之间的内容

2.1 控制语句

1)continue:跳过所在的本次循环,继续下一次循环。
2)break:用于结束当前所在的整个循环。
3)exit:直接跳出程序

2.2 eg

eg1:输入一个正整数,判断是否为质数(素数)

质数:只能被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

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
read -p "请输入一个正整数字:" number

[ $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是质数" && exit

bash -x for6.sh

eg2:输入一个年份,判断是否是润年(能被4整除但不能被100整除,或能被400整除的年份即为闰年。)

1
2
3
4
5
6
7
8
9
#!/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

3、while循环

条件为真进入循环;条件为假退出循环

3.1 语法结构

1
2
3
4
while 表达式
do
command...
done

3.2 案例

eg:打印1-5数字

1)for循环:

1
2
3
4
for ((i=1;i<=5;i++))
do
echo $i
done

2)while循环:

1
2
3
4
5
6
i=1
while [ $i -le 5 ]
do
echo $i
let i++
done

4、until循环

4.1 语法结构

条件为假进入循环;条件为真退出循环,与while循环相反。

1
2
3
4
5
6
until 条件表达式
do
command
command
...
done

eg:打印1-5

1)while循环:

1
2
3
4
5
6
i=1
while [ $i -le 5 ]
do
echo $i
let i++
done

2)until循环:

1
2
3
4
5
6
i=1
until (( $i > 5 ))
do
echo $i
let i++
done

4.2 案例

使用until语句批量创建10个用户,要求stu1-stu5用户的UID分别为1001-1005;stu6~stu10用户的家目录分别在/rhome/stu6-/rhome/stu10

1
2
3
4
5
6
7
8
9
10
11
12
#!/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