警告
本文最后更新于 2023-10-22,文中内容可能已过时。
Bash 循环
Bash 提供三种循环语法for
、while
和until
。
for
的语法是for var in list;do command; done
和for (( expression1; expression2; expression3 )) ; do command; done
while
和until
的语法都是xxx condition;do command; done
,xxx 可替换为while
和until
而if
的语法是if condition;then command;fi
case
的语法是case expression in ... esac
这很明显是两种命令风格。
while 循环
while
循环有一个判断条件,只要符合条件,就不断循环执行指定的语句。
1
2
3
|
while condition; do
commands
done
|
上面代码中,只要满足条件condition
,就会执行命令commands
。然后,再次判断是否满足条件condition
,只要满足,就会一直执行下去。只有不满足条件,才会退出循环。
循环条件condition
可以使用test
命令,跟if
结构的判断条件写法一致。
关于if
名,请参考《Bash 条件判断》的判断表达式
小节
关键字do
可以跟while
不在同一行,这时两者之间不需要使用分号分隔。一般都会写在同一行
创建脚本while_loop.sh
1
2
3
4
5
6
7
|
#!/usr/bin/env bash
num=10
while (( num > 0 ));do
echo nnmber is $num
(( num-- ))
done
|
赋值后执行
1
2
3
4
5
6
7
8
9
10
11
|
$ ./while_loop.sh
nnmber is 10
nnmber is 9
nnmber is 8
nnmber is 7
nnmber is 6
nnmber is 5
nnmber is 4
nnmber is 3
nnmber is 2
nnmber is 1
|
如果while
的条件为true
,例如while true;do echo "knock knock";sleep 1;done
,则循环体会无限循环,可以按下 Ctrl + c 停止。
while
的条件部分可以为任意数量的命令,这些命令都会执行,但是整个判断条件执行结果的真伪只看最后一个命令的执行结果。
1
|
$ while true; false; do echo 'Hi, looping ...'; done
|
上面代码运行后,不会有任何输出,因为while
的最后一个命令是false
。
这在学习if
命令的时候,就了解过这个结论,具体请看《Bash 条件判断》
所以循环体也可以写成这样
1
2
3
4
5
6
|
#!/usr/bin/env bash
num=10
while (( num-- ));(( num > 0 ));do
echo nnmber is $num
done
|
赋权并输出
1
2
3
4
5
6
7
8
9
10
|
$ ./while_loop.sh
nnmber is 9
nnmber is 8
nnmber is 7
nnmber is 6
nnmber is 5
nnmber is 4
nnmber is 3
nnmber is 2
nnmber is 1
|
until 循环
until
循环与while
循环恰好相反,只要不符合判断条件(判断条件失败),就不断循环执行指定的语句。一旦符合判断条件,就退出循环。
跟while
循环就一字之差,特性上完全一直。
1
2
3
|
until condition; do
commands
done
|
关键字do
可以与until
不写在同一行,这时两者之间不需要分号分隔。
1
2
3
4
|
until condition
do
commands
done
|
简单实践如下
创建脚本until_loop.sh
1
2
3
4
5
6
|
#!/usr/bin/env bash
num=10
until (( num-- )); (( num < 0 )) ;do
echo nnmber is $num
done
|
赋权后执行
1
2
3
4
5
6
7
8
9
10
11
|
$ ./until_loop.sh
nnmber is 9
nnmber is 8
nnmber is 7
nnmber is 6
nnmber is 5
nnmber is 4
nnmber is 3
nnmber is 2
nnmber is 1
nnmber is 0
|
until
的条件部分也可以是一个命令,表示在这个命令执行成功之前,不断重复尝试。
1
2
3
4
|
until cp $1 $2; do
echo 'Attempt to copy failed. waiting...'
sleep 5
done
|
上面例子表示,只要cp $1 $2
这个命令执行不成功,就 5 秒钟后再尝试一次,直到成功为止。
until
循环都可以转为while
循环,只要把条件设为否定即可。上面这个例子可以改写如下。
1
2
3
4
|
while ! cp $1 $2; do
echo 'Attempt to copy failed. waiting...'
sleep 5
done
|
一般来说,until
用得比较少,完全可以统一都使用while
。
for…in 循环
for...in
循环用于遍历列表的每一项。
1
2
3
4
|
for variable in list
do
commands
done
|
关键词do
可以跟for
写在同一行,两者使用分号分隔。
1
2
3
|
for variable in list; do
commands
done
|
上面语法中,for
循环会依次从list
列表中取出一项,作为变量variable
,然后在循环体中进行处理。
下面是一个例子。
1
2
3
4
5
|
#!/bin/bash
for i in word1 word2 word3; do
echo $i
done
|
上面例子中,word1 word2 word3
是一个包含三个单词的列表,in 后面用逗号分隔的多个项,就是列表
变量i
依次等于word1
、word2
、word3
,命令echo $i
则会相应地执行三次。
列表可以由通配符产生。
1
2
3
|
for i in *.png; do
ls -l $i
done
|
上面例子中,*.png
会替换成当前目录中所有 PNG 图片文件,变量i
会依次等于每一个文件。
for 循环经常跟大括号拓展(例如{1..3}
)配合使用
具体请看《Bash 的模式拓展》的大括号扩展
小节
例如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
$ for num in {1..10};do echo number is $num ;done
number is 1
number is 2
number is 3
number is 4
number is 5
number is 6
number is 7
number is 8
number is 9
number is 10
$ for num in {1..10..4};do echo number is $num ;done
number is 1
number is 5
number is 9
$ for num in {10..1..3};do echo number is $num ;done
number is 10
number is 7
number is 4
number is 1
|
列表也可以通过子命令产生。比如我们可以通过cat
命令查看文本文件中所有的单词,比如
文件/opt/shell_script/text.txt
:
1
2
3
4
|
aaa bbb cccc
dddd eee fff ggg hhhh iiii
1111 2222 333
Bash 真的 很好用,非常推荐
|
读取文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
$ for word in $(cat /opt/shell_script/text.txt);do echo $word;done
aaa
bbb
cccc
dddd
eee
fff
ggg
hhhh
iiii
1111
2222
333
Bash
真的
很好用,非常推荐
|
我们之前在《Bash read 命令》中学习过通过read
命令读取文件。
我们在脚本中经常利用for...in
循环来遍历脚本的所有参数for param in "$@";do done
,同时in list
的部分可以省略,这时list
默认等于脚本的所有参数$@
。但是,为了可读性,最好还是不要省略,参考下面的例子。
1
2
3
4
5
6
7
8
9
|
for filename; do
echo "$filename"
done
# 等同于
for filename in "$@" ; do
echo "$filename"
done
|
在函数体中也是一样的,for...in
循环省略in list
的部分,则list
默认等于函数的所有参数。但是最好不要省略。
for 循环
for
循环还支持 C 语言的循环语法。
1
2
3
|
for (( expression1; expression2; expression3 )); do
commands
done
|
上面代码中,expression1
用来初始化循环条件,expression2
用来决定循环结束的条件,expression3
在每次循环迭代的末尾执行,用于更新值。
注意,循环条件放在双重圆括号之中。另外,圆括号之中使用变量,不必加上美元符号$
。
它等同于下面的while
循环。
1
2
3
4
5
|
(( expression1 ))
while (( expression2 )); do
commands
(( expression3 ))
done
|
for
条件部分的三个语句,都可以省略。
1
2
3
4
5
6
7
|
for ((;;))
do
read var
if [ "$var" = "." ]; then
break
fi
done
|
上面脚本会反复读取命令行输入,直到用户输入了一个点(.
)为止,才会跳出循环。
简单实践如下:
1
2
3
4
5
6
7
8
9
10
11
|
$ for ((i=0;i<10;i++ ));do echo number is $i;done
number is 0
number is 1
number is 2
number is 3
number is 4
number is 5
number is 6
number is 7
number is 8
number is 9
|
使用起来跟 Java 中的 for 循环一样,非常好用。
break,continue
Bash 提供了两个内部命令break
和continue
,用来在循环内部跳出循环。
简单实验如下:
创建脚本./break_continue.sh
1
2
3
4
5
6
7
8
9
10
|
#!/usr/bin/env bash
for index in {1..10};do
if [ $index -eq 3 ];then
continue
elif [ $index -eq 5 ];then
break
fi
echo $index
done
|
赋权后执行
1
2
3
4
|
$ ./break_continue.sh
1
2
4
|
select 结构
用来做功能脚本的菜单非常好用
select
结构主要用来生成简单的菜单。它的语法与for...in
循环基本一致。
1
2
3
|
select name [in list] ;do
commands
done
|
select 和 do 也可以写成两行,这样就不需要用分号隔开
1
2
3
4
5
|
select name
[in list]
do
commands
done
|
Bash 会对select
依次进行下面的处理。
select
生成一个菜单,内容是列表list
的每一项,并且每一项前面还有一个数字编号。
- Bash 提示用户选择一项,输入它的编号。
- 用户输入以后,Bash 会将该项的内容存在变量
name
,该项的编号存入环境变量REPLY
。如果用户没有输入,就按回车键,Bash 会重新输出菜单,让用户选择。
- 执行命令体
commands
。commands
中可以使用break
跳出循环。
- 执行结束后,回到第一步,重复这个过程。直到用户按下
Ctrl + c
,退出执行。
通常commands
部分是一个case
命令,针对不同项,执行不同的命令。
输入提示符由变量PS3
控制,默认是#?
,我们可以对此进行自定义,比如PS3="please input menu num > "
关于PS3
以及相关提示符的介绍,请看《Bash 命令提示符》
简单实践如下:
创建select.sh
:
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
|
#!/usr/bin/env bash
echo "select function"
PS3="please input menu num > "
data=()
select func in query add delete exit_fun ; do
case $func in
query)
echo ${data[@]}
;;
add)
next_val=${#data[@]}
data[$next_val]="$((++next_val))"
echo ${data[@]}
;;
delete)
last_index=0
for val_index in ${!data[@]};do
last_index=val_index
done
unset data[$last_index]
echo ${data[@]}
;;
exit_fun)
break
;;
esac
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
|
$ ./select.sh
select function
1) query
2) add
3) delete
4) exit_fun
please input menu num > 1
please input menu num > 2
1
please input menu num > 2
1 2
please input menu num > 2
1 2 3
please input menu num > 2
1 2 3 4
please input menu num > 3
1 2 3
please input menu num > 3
1 2
please input menu num > 3
1
please input menu num > 3
please input menu num > 4
|
此外,我们可以将一个数组变量放到list
的位置,动态生成菜单。