循环

循环其实不足为奇。跟其它程序设计语言一样,bash中的循环也是只要控制条件为真就一直迭代执行的代码块。

Bash中有四种循环:for,while,until和select。

for循环

for与它在C语言中的姊妹非常像。看起来是这样:

1. for arg in elem1 elem2 ... elemN
2. do
3. # 语句
4. done

在每次循环的过程中,arg依次被赋值为从elem1到elemN。这些值还可以是通配符或者大括号扩展。

当然,我们还可以把for循环写在一行,但这要求do之前要有一个分号,就像下面这样:

1. for i in {1..5}; do echo $i; done

还有,如果你觉得for..in..do对你来说有点奇怪,那么你也可以像C语言那样使用for,比如:

1. for (( i = 0; i < 10; i++ )); do
2. echo $i
3. done

当我们想对一个目录下的所有文件做同样的操作时,for就很方便了。举个例子,如果我们想把所有的.bash文件移动到script文件夹中,并给它们可执行权限,我们的脚本可以这样写:

1. #!/bin/bash

3. for FILE in $HOME/*.bash; do
4. mv "$FILE" "${HOME}/scripts"
5. chmod +x "${HOME}/scripts/${FILE}"
6. done

while循环

while循环检测一个条件,只要这个条件为 真,就执行一段命令。被检测的条件跟if..then中使用的基元并无二异。因此一个while循环看起来会是这样:

1. while [[ condition ]]
2. do
3. # 语句
4. done

跟for循环一样,如果我们把do和被检测的条件写到一行,那么必须要在do之前加一个分号。

比如下面这个例子:

1. #!/bin/bash

3. # 0到9之间每个数的平方
4. x=0
5. while [[ $x -lt 10 ]]; do # x小于10
6. echo $(( x * x ))
7. x=$(( x + 1 )) # x加1
8. done

until循环

until循环跟while循环正好相反。它跟while一样也需要检测一个测试条件,但不同的是,只要该条件为 假 就一直执行循环:

1. until [[ condition ]]; do
2. # 语句
3. done

select循环

select循环帮助我们组织一个用户菜单。它的语法几乎跟for循环一致:

1. select answer in elem1 elem2 ... elemN
2. do
3. # 语句
4. done

select会打印elem1..elemN以及它们的序列号到屏幕上,之后会提示用户输入。通常看到的是$?(PS3变量)。用户的选择结果会被保存到answer中。如果answer是一个在1..N之间的数字,那么语句会被执行,紧接着会进行下一次迭代 —— 如果不想这样的话我们可以使用break语句。

一个可能的实例可能会是这样:

1. #!/bin/bash

3. PS3="Choose the package manager: "
4. select ITEM in bower npm gem pip
5. do
6. echo -n "Enter the package name: " && read PACKAGE
7. case $ITEM in
8. bower) bower install $PACKAGE ;;
9. npm)   npm   install $PACKAGE ;;
10. gem)   gem   install $PACKAGE ;;
11. pip)   pip   install $PACKAGE ;;
12. esac
13. break # 避免无限循环
14. done

这个例子,先询问用户他想使用什么包管理器。接着,又询问了想安装什么包,最后执行安装操作。

运行这个脚本,会得到如下输出:

1. $ ./my_script
2. 1) bower
3. 2) npm
4. 3) gem
5. 4) pip
6. Choose the package manager: 2
7. Enter the package name: bash-handbook
8. <installing bash-handbook>

循环控制

我们会遇到想提前结束一个循环或跳过某次循环执行的情况。这些可以使用shell内建的break和continue语句来实现。它们可以在任何循环中使用。

break语句用来提前结束当前循环。我们之前已经见过它了。

continue语句用来跳过某次迭代。我们可以这么来用它:

1. for (( i = 0; i < 10; i++ )); do
2. if [[ $(( i % 2 )) -eq 0 ]]; then continue; fi
3. echo $i
4. done

运行上面的例子,会打印出所有0到9之间的奇数。