外观
控制语句
条件语句
跟其它程序设计语言一样,Bash 中的条件语句让我们可以决定一个操作是否被执行。结果取决于一个包在[[ ]]里的表达式。
由[[ ]](sh中是[ ])包起来的表达式被称作 检测命令 或 基元。这些表达式帮助我们检测一个条件的结果。这里可以找到有关bash 中单双中括号区别的答案。
共有两个不同的条件表达式:if和case。
if
(1)if 语句
if在使用上跟其它语言相同。如果中括号里的表达式为真,那么then和fi之间的代码会被执行。fi标志着条件代码块的结束。
# 写成一行
if [[ 1 -eq 1 ]]; then echo "1 -eq 1 result is: true"; fi
# Output: 1 -eq 1 result is: true
# 写成多行
if [[ "abc" -eq "abc" ]]
then
echo ""abc" -eq "abc" result is: true"
fi
# Output: abc -eq abc result is: true(2)if else 语句
同样,我们可以使用if..else语句,例如:
if [[ 2 -ne 1 ]]; then
echo "true"
else
echo "false"
fi
# Output: true(3)if elif else 语句
有些时候,if..else不能满足我们的要求。别忘了if..elif..else,使用起来也很方便。
x=10
y=20
if [[ ${x} > ${y} ]]; then
echo "${x} > ${y}"
elif [[ ${x} < ${y} ]]; then
echo "${x} < ${y}"
else
echo "${x} = ${y}"
fi
# Output: 10 < 20⌨️ 『示例源码』 if-demo.sh
case
如果你需要面对很多情况,分别要采取不同的措施,那么使用case会比嵌套的if更有用。使用case来解决复杂的条件判断,看起来像下面这样:
⌨️ 『示例源码』 case-demo.sh
exec
case ${oper} in
"+")
val=`expr ${x} + ${y}`
echo "${x} + ${y} = ${val}"
;;
"-")
val=`expr ${x} - ${y}`
echo "${x} - ${y} = ${val}"
;;
"*")
val=`expr ${x} \* ${y}`
echo "${x} * ${y} = ${val}"
;;
"/")
val=`expr ${x} / ${y}`
echo "${x} / ${y} = ${val}"
;;
*)
echo "Unknown oper!"
;;
esac每种情况都是匹配了某个模式的表达式。|用来分割多个模式,)用来结束一个模式序列。第一个匹配上的模式对应的命令将会被执行。*代表任何不匹配以上给定模式的模式。命令块儿之间要用;;分隔。
循环语句
循环其实不足为奇。跟其它程序设计语言一样,bash 中的循环也是只要控制条件为真就一直迭代执行的代码块。
Bash 中有四种循环:for,while,until和select。
for循环
for与它在 C 语言中的姊妹非常像。看起来是这样:
for arg in elem1 elem2 ... elemN
do
### 语句
done在每次循环的过程中,arg依次被赋值为从elem1到elemN。这些值还可以是通配符或者大括号扩展。
当然,我们还可以把for循环写在一行,但这要求do之前要有一个分号,就像下面这样:
for i in {1..5}; do echo $i; done还有,如果你觉得for..in..do对你来说有点奇怪,那么你也可以像 C 语言那样使用for,比如:
for (( i = 0; i < 10; i++ )); do
echo $i
done当我们想对一个目录下的所有文件做同样的操作时,for就很方便了。举个例子,如果我们想把所有的.bash文件移动到script文件夹中,并给它们可执行权限,我们的脚本可以这样写:
DIR=/home/zp
for FILE in ${DIR}/*.sh; do
mv "$FILE" "${DIR}/scripts"
done
# 将 /home/zp 目录下所有 sh 文件拷贝到 /home/zp/scripts⌨️ 『示例源码』 for-demo.sh
while循环
while循环检测一个条件,只要这个条件为 真,就执行一段命令。被检测的条件跟if..then中使用的基元并无二异。因此一个while循环看起来会是这样:
while [[ condition ]]
do
### 语句
done跟for循环一样,如果我们把do和被检测的条件写到一行,那么必须要在do之前加一个分号。
比如下面这个例子:
### 0到9之间每个数的平方
x=0
while [[ ${x} -lt 10 ]]; do
echo $((x * x))
x=$((x + 1))
done
# Output:
# 0
# 1
# 4
# 9
# 16
# 25
# 36
# 49
# 64
# 81⌨️ 『示例源码』 while-demo.sh
until循环
until循环跟while循环正好相反。它跟while一样也需要检测一个测试条件,但不同的是,只要该条件为 假 就一直执行循环:
x=0
until [[ ${x} -ge 5 ]]; do
echo ${x}
x=`expr ${x} + 1`
done
# Output:
# 0
# 1
# 2
# 3
# 4⌨️ 『示例源码』 until-demo.sh
select循环
select循环帮助我们组织一个用户菜单。它的语法几乎跟for循环一致:
select answer in elem1 elem2 ... elemN
do
### 语句
doneselect会打印elem1..elemN以及它们的序列号到屏幕上,之后会提示用户输入。通常看到的是$?(PS3变量)。用户的选择结果会被保存到answer中。如果answer是一个在1..N之间的数字,那么语句会被执行,紧接着会进行下一次迭代 —— 如果不想这样的话我们可以使用break语句。
一个可能的实例可能会是这样:
#!/usr/bin/env bash
PS3="Choose the package manager: "
select ITEM in bower npm gem pip
do
echo -n "Enter the package name: " && read PACKAGE
case ${ITEM} in
bower) bower install ${PACKAGE} ;;
npm) npm install ${PACKAGE} ;;
gem) gem install ${PACKAGE} ;;
pip) pip install ${PACKAGE} ;;
esac
break # 避免无限循环
done这个例子,先询问用户他想使用什么包管理器。接着,又询问了想安装什么包,最后执行安装操作。
运行这个脚本,会得到如下输出:
$ ./my_script
1) bower
2) npm
3) gem
4) pip
Choose the package manager: 2
Enter the package name: gitbook-cli⌨️ 『示例源码』 select-demo.sh
break 和 continue
如果想提前结束一个循环或跳过某次循环执行,可以使用 shell 的break和continue语句来实现。它们可以在任何循环中使用。
break语句用来提前结束当前循环。
continue语句用来跳过某次迭代。
⌨️ 『示例源码』 break-demo.sh
# 查找 10 以内第一个能整除 2 和 3 的正整数
i=1
while [[ ${i} -lt 10 ]]; do
if [[ $((i % 3)) -eq 0 ]] && [[ $((i % 2)) -eq 0 ]]; then
echo ${i}
break;
fi
i=`expr ${i} + 1`
done
# Output: 6⌨️ 『示例源码』 continue-demo.sh
# 打印10以内的奇数
for (( i = 0; i < 10; i ++ )); do
if [[ $((i % 2)) -eq 0 ]]; then
continue;
fi
echo ${i}
done
# Output:
# 1
# 3
# 5
# 7
# 9