# 循环(loop)

循环可以不断执行某个程序段楼,直到用户设定的条件达成为止。

# while do done、until do done(不定循环)

当条件成立时,执行循环体

while [ condition ]   # 中括号中条件判断
do    # 循环开始
  程序段落
done  # 循环结束
1
2
3
4

还有一种不定循环的方式,当条件成立时退出循环体

until [ condition ]
do
    程序段落
done
1
2
3
4

范例:让使用者输入 yes 或则是 YES 才结束程序的执行,否则就一直告知用户输入字符串

vim yes_to_stop.sh

#!/bin/bash
# Program:
#       
# History:
#       2020/02/12              mrcode          first relese
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

while [ "${yn}" != 'yes' -a "${yn}" != 'YES' ]
# 使用 until 则是如下
# until [ "${yn}" == 'yes' -o "${yn}" == 'YES' ]
do
  read -p '请输入 yes 或 YES 退出程序' yn
done

echo "你输入了正确答案"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

测试如下

[mrcode@study bin]$ ./yes_to_stop.sh
请输入 yes 或 YES 退出程序j
请输入 yes 或 YES 退出程序jj
请输入 yes 或 YES 退出程序yes
你输入了正确答案
1
2
3
4
5

如果想要计算 1+2+3+..100则如下写

vim cal_1_100.sh

#!/bin/bash
# Program:
#       计算 1+2+3+..100 的结果
# History:
#       2020/02/12              mrcode          first relese
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

total=0	# 计算结果
i=0			# 当前数值

while [ "${i}" != 100 ]
do
	i=$(($i+1))		# 每次增加 1
	total=$(($total+$i))
done
echo "1+2+3+..100 = ${total}"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# for...do...done 固定循环

for var in con1 con2 con3 ...
do
	循环体
done
1
2
3
4

范例:假设有三种动物,分别是 dog、cat、elephant 三种,输出三行信息,如 There are dogs... 之类的信息

vim show_animal.sh

#!/bin/bash
# Program:
#       
# History:
#       2020/02/12              mrcode          first relese
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

for animal in dog cat elephant
do
	echo "There are ${animal}s..."
done
1
2
3
4
5
6
7
8
9
10
11
12
13
14

/etc/passwd 中第一个字段存放了用户名,使用循环打印出每个用户名的 id 信息;可使用 cut 截取第一字段,使用 id指令获取用户名的信息(标识符与特殊参数)

vim userid.sh

#!/bin/bash
# Program:
#       
# History:
#       2020/02/12              mrcode          first relese
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

users=$(cut -d ':' -f1 /etc/passwd)		# 获取到所有的用户名
for user in ${users}
do
	id ${user}
done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

当然还可以使用数字来做循环项,比如需要执行 ping 192.168.1.1~192.168.1.100 也就是从 1 ping 到 100,但是不可能需要我们手动输入 100 个数字吧

vim pingip.sh


#!/bin/bash
# Program:
#       
# History:
#       2020/02/12              mrcode          first relese
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

network="192.168.0"			# 先定义一个网域的前部分
for sitenu in $(seq 1 100)	# seq 为 sequence 连续的意思
do
	# ping -c 1 -w 1 192.168.0.101 &> /dev/null && echo "1" || echo "0"
	# 不显示执行结果,并获取命令是否执行成功
	ping -c 1 -w 1 ${network}.${sitenu} &> /dev/null && result=0 || result=1
	if [ "${result}" == 0 ]; then
		echo "${network}.${sitenu} is up"
	else
		echo "${network}.${sitenu} is down"
	fi
done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

测试结果

[mrcode@study bin]$ ./pingip.sh
192.168.0.1 is up
192.168.0.2 is down
192.168.0.3 is down
..
1
2
3
4
5

对于 $(seq 1 100) 来说,还可以使用 bash 的内建机制 {1..100} 来代替,中间两个点表示连续的意思,比如想要输出 a~g 则可以使用 a..g

最后一个范例:

  1. 让用户输入一个目录
  2. 如果目录不存在,则提示并退出程序
  3. 如果目录存在,则获取该目录下第一级文件是否可读、可写、可执行
vim dir_perm.sh

#!/bin/bash
# Program:
#       
# History:
#       2020/02/12              mrcode          first relese
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

read -p "请输入一个目录,将会检测该目录是否可读、可写、可执行:" dir
# 判定输入不为空,并且目录存在
if [ "${dir}" == '' -o ! -d "${dir}" ]; then
        echo "The ${dir} is NOT exist in your system"
        exit 1
fi

# 获取该目录下的文件权限信息
filelist=$(ls ${dir})
for file in ${filelist}
do
        perm=""
        test -r "${dir}/${file}" && perm="${perm} readable"
        test -w "${dir}/${file}" && perm="${perm} writable"
        test -x "${dir}/${file}" && perm="${perm} executable"
        echo "The file ${dir}/${file}'s permission is ${perm}"
done

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

使用这种方式,可以很轻易的来处理一些文件的特性

# for...do...done 数值处理

for (( 初始值; 限制值; 执行步阶))
do
	循环体
done
1
2
3
4
  • 初始值:某个变量在循环中的起始值,可以以 i=1 设置好初始值
  • 限制值:当变量值在这个限制值范围内,则继续循环。例如 i<=100
  • 执行步阶:每执行一次循环时,变量的变化量。例如 i=i+1,如果是自增则可以使用 i++ 来替代

范例:计算从 1 累加到指定数值的结果

vim cal_1_100-2.sh

#!/bin/bash
# Program:
#       
# History:
#       2020/02/12              mrcode          first relese
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

read -p "请输入一个数值,将计算出从 1 累加到该数值的计算结果" nu

total=0

for (( i=1; i<=${nu}; i++))
do
	total=$((${total}+${i}))
done

echo "1+..+${nu} = ${total}"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

测试输出如下

[mrcode@study bin]$ ./cal_1_100-2.sh
请输入一个数值,将计算出从 1 累加到该数值的计算结果2
1+..+2 = 3
[mrcode@study bin]$ ./cal_1_100-2.sh
请输入一个数值,将计算出从 1 累加到该数值的计算结果100
1+..+100 = 5050
1
2
3
4
5
6

# 搭配随机数与数组的实验

现在大概已经能够掌握 shell script 了。

现在来做个有趣的小东西,今天中午吃啥?要完成这个脚本,首先需要将全部的店家输入到一组数组中,再通过随机数的处理,获得可能的值

vim what_to_eat.sh

#!/bin/bash
# Program:
#       
# History:
#       2020/02/12              mrcode          first relese
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# 定义你搜集到的店家信息
eat[1]="卖当当汉堡"
eat[2]="肯爷爷炸鸡"
eat[3]="彩虹日式便当"
eat[4]="越油越好吃打呀"
eat[5]="想不出吃什么"
eat[6]="太师傅便当"
eat[7]="池上便当"
eat[8]="怀恋火车便当"
eat[9]="一起吃泡面"
eat[10]="太上皇"
eatnum=10		# 一共有几家可用的店铺

check=$((${RANDOM} * ${eatnum} / 32767 + 1))
echo "your may eat ${eat[${check}]}"
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

测试输出

[mrcode@study bin]$ ./what_to_eat.sh
your may eat 太上皇
[mrcode@study bin]$ ./what_to_eat.sh
your may eat 越油越好吃打呀
[mrcode@study bin]$ ./what_to_eat.sh
your may eat 想不出吃什么
[mrcode@study bin]$ ./what_to_eat.sh
1
2
3
4
5
6
7

继续深入,一次性输出 3 个选择,并且不能重复

vim what_to_eat-2.sh

#!/bin/bash
# Program:
#       
# History:
#       2020/02/12              mrcode          first relese
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# 定义你搜集到的店家信息
eat[1]="卖当当汉堡"
eat[2]="肯爷爷炸鸡"
eat[3]="彩虹日式便当"
eat[4]="越油越好吃打呀"
eat[5]="想不出吃什么"
eat[6]="太师傅便当"
eat[7]="池上便当"
eat[8]="怀恋火车便当"
eat[9]="一起吃泡面"
eat[10]="太上皇"
eatnum=10		# 一共有几家可用的店铺

# 其实就是需要轮询出来 3 个不同的索引结果

eated=0	# 已选中数量

while [ "${eated}" -lt 3 ];
do
	check=$((${RANDOM} * ${eatnum} / 32767 + 1))
	mycheck=0		# 当为 0 时,表示不重复
	# 去重检查
	if [ ${eated} -gt 0 ]; then			# 当已选中至少一个店铺的时候,才执行
    for i in $(seq 1 ${eated})
    do
      if [ "${eatedcon[$i]}" == $check ]; then
        mycheck=1
      fi
    done
  fi
	if [ ${mycheck} == 0 ]; then
		echo "your may eat ${eat[${check}]}"
		eated=$(( ${eated} + 1 ))
		eatedcon[${eated}]=${check}		# 将已选中结果存储起来
	fi
done
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