选择结构:分别是 if else 语句和 case in 语句。
- if condition
- then
- statement(s)
- fi
condition是判断条件,如果 condition 成立(返回“真”),那么 then 后边的语句将会被执行;如果 condition 不成立(返回“假”),那么不会执行任何语句。
- if condition; then
- statement(s)
- fi
请注意 condition 后边的分号;,当 if 和 then 位于同一行的时候,这个分号是必须的,否则会有语法错误。
- if condition
- then
- statement1
- else
- statement2
- fi
如果 condition 成立,那么 then 后边的 statement1 语句将会被执行;否则,执行 else 后边的 statement2 语句。
- if condition1
- then
- statement1
- elif condition2
- then
- statement2
- elif condition3
- then
- statement3
- ……
- else
- statementn
- fi
注意,if 和 elif 后边都得跟着 then。
- case expression in
- pattern1)
- statement1
- ;;
- pattern2)
- statement2
- ;;
- pattern3)
- statement3
- ;;
- ……
- *)
- statementn
- esac
case、in 和 esac 都是 Shell 关键字,expression 表示表达式,pattern 表示匹配模式。
case 会将 expression 的值与 pattern1、pattern2、pattern3 逐个进行匹配:
case in 的 pattern 部分支持简单的正则表达式,具体来说,可以使用以下几种格式:
格式 | 说明 |
---|---|
* | 表示任意字符串。 |
[abc] | 表示 a、b、c 三个字符中的任意一个。比如,[15ZH] 表示 1、5、Z、H 四个字符中的任意一个。 |
[m-n] | 表示从 m 到 n 的任意一个字符。比如,[0-9] 表示任意一个数字,[0-9a-zA-Z] 表示字母或数字。 |
| | 表示多重选择,类似逻辑运算中的或运算。比如,abc | xyz 表示匹配字符串 "abc" 或者 "xyz"。 |
对*)的几点说明:
Shell case in 语句中的*)用来“托底”,万一 expression 没有匹配到任何一个模式,*)部分可以做一些“善后”工作,或者给用户一些提示。
可以没有*)部分。如果 expression 没有匹配到任何一个模式,那么就不执行任何操作。
除最后一个分支外(这个分支可以是普通分支,也可以是*)分支),其它的每个分支都必须以;;结尾,;;代表一个分支的结束,不写的话会有语法错误。最后一个分支可以写;;,也可以不写,因为无论如何,执行到 esac 都会结束整个 case in 语句。
最后一个分支*)并不是什么语法规定,它只是一个正则表达式,*表示任意字符串,所以不管 expression 的值是什么,*)总能匹配成功。
循环结构:
- while condition
- do
- statements
- done
while 循环的执行流程为:
unti 循环和 while 循环恰好相反,当判断条件不成立时才进行循环,一旦判断条件成立,就终止循环。
- until condition
- do
- statements
- done
一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。
- for var in value_list
- do
- command1
- command2
- ...
- commandN
- done
当变量值在列表里,for 循环即执行一次所有命令,使用变量名获取列表中的当前取值。in 列表可以包含替换、字符串和文件名。
in列表是可选的,如果不用它,for循环使用命令行的位置参数。
对 value_list 的说明
取值列表 value_list 的形式有多种,你可以直接给出具体的值,也可以给出一个范围,还可以使用命令产生的结果,甚至使用通配符
直接给出具体的值:可以在 in 关键字后面直接给出具体的值,多个值之间以空格分隔 ;
给出一个取值范围:{start..end} 注意中间用两个点号相连,这种形式只支持数字和字母;
使用命令的执行结果:使用反引号``
或者$()
都可以取得命令的执行结果;
使用 Shell 通配符:一种精简化的正则表达式,通常用来匹配目录或者文件,而不是文本;
使用特殊变量:例如 $#、$*、$@、$?、$$ 等,可省略,省略后效果与$@一样。
- for((exp1; exp2; exp3))
- do
- statements
- done
for 循环中的三个表达式
for 循环中的 exp1(初始化语句)、exp2(判断条件)和 exp3(自增或自减)都是可选项,都可以省略(但分号;
必须保留)。
- while :
- do
- command
- done
- #------------------
- while true
- do
- command
- done
- #------------------
- for (( ; ; ))
select in 循环用来增强交互性,它可以显示出带编号的菜单,用户输入不同的编号就可以选择不同的菜单,并执行不同的功能。
select in 是 Shell 独有的一种循环,非常适合终端(Terminal)这样的交互场景
- select variable in value_list
- do
- statements
- done
举例:
- #!/bin/bash
- echo "What is your favourite OS?"
- select name in "Linux" "Windows" "Mac OS" "UNIX" "Android"
- do
- echo $name
- done
- echo "You have selected $name"
-
- 运行结果:
- What is your favourite OS?
- 1) Linux
- 2) Windows
- 3) Mac OS
- 4) UNIX
- 5) Android
- #? 4↙
- You have selected UNIX
- #? 1↙
- You have selected Linux
- #? 9↙
- You have selected
- #? 2↙
- You have selected Windows
- #?^D
#?
用来提示用户输入菜单编号;^D
表示按下 Ctrl+D 组合键,它的作用是结束 select in 循环。
运行到 select 语句后,取值列表 value_list 中的内容会以菜单的形式显示出来,用户输入菜单编号,就表示选中了某个值,这个值就会赋给变量 variable,然后再执行循环体中的 statements(do 和 done 之间的部分)。
每次循环时 select 都会要求用户输入菜单编号,并使用环境变量 PS3 的值作为提示符,PS3 的默认值为#?
,修改 PS3 的值就可以修改提示符。
如果用户输入的菜单编号不在范围之内,例如上面我们输入的 9,那么就会给 variable 赋一个空值;如果用户输入一个空值(什么也不输入,直接回车),会重新显示一遍菜单。
注意,select 是无限循环(死循环),输入空值,或者输入的值无效,都不会结束循环,只有遇到 break 语句,或者按下 Ctrl+D 组合键才能结束循环。
在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell 使用两个命令来实现该功能:break 和 continue
在C语言、C++、C#、Python、Java 等大部分编程语言中,break 和 continue 只能跳出当前层次的循环,内层循环中的 break 和 continue 对外层循环不起作用;但是 Shell 中的 break 和 continue 却能够跳出多层循环,也就是说,内层循环中的 break 和 continue 能够跳出外层循环。
语法:break n
n 表示跳出循环的层数,如果省略 n,则表示跳出当前的整个循环。break 关键字通常和 if 语句一起使用,即满足条件时便跳出循环。
例1:如果 break 后面不跟数字的话,表示跳出当前循环,对于有两层嵌套的循环,就得使用两个 break 关键字。
- #!/bin/bash
- i=0
- while ((++i)); do #外层循环
- if((i>4)); then
- break #跳出外层循环
- fi
- j=0;
- while ((++j)); do #内层循环
- if((j>4)); then
- break #跳出内层循环
- fi
- printf "%-4d" $((i*j))
- done
- printf "\n"
- done
例2:在 break 后面跟一个数字,让它一次性地跳出两层循环
- #!/bin/bash
- i=0
- while ((++i)); do #外层循环
- j=0;
- while ((++j)); do #内层循环
- if((i>4)); then
- break 2 #跳出内外两层循环
- fi
- if((j>4)); then
- break #跳出内层循环
- fi
- printf "%-4d" $((i*j))
- done
- printf "\n"
- done
修改后的代码将所有 break 都移到了内层循环里面。读者需要重点关注break 2
这条语句,它使得程序可以一次性跳出两层循环,也就是先跳出内层循环,再跳出外层循环。
语法:continue n
n 表示循环的层数:
【实例】使用 continue 跳出多层循环,请看下面的代码:
- #!/bin/bash
- for((i=1; i<=5; i++)); do
- for((j=1; j<=5; j++)); do
- if((i*j==12)); then
- continue 2
- fi
- printf "%d*%d=%-4d" $i $j $((i*j))
- done
- printf "\n"
- done
从运行结果可以看出,遇到continue 2
时,不但跳过了内层 for 循环,也跳过了外层 for 循环。
Shell 函数的本质是一段可以重复使用的脚本代码,这段代码被提前编写好了,放在了指定的位置,使用时直接调取即可。
Shell 函数定义的语法格式如下:
- function name() {
- statements
- [return value]
- }
对各个部分的说明:
function
是 Shell 中的关键字,专门用来定义函数;name
是函数名;statements
是函数要执行的代码,也就是一组语句;return value
表示函数的返回值,其中 return 是 Shell 关键字,专门用在函数中返回一个值;这一部分可以写也可以不写。由{ }
包围的部分称为函数体,调用一个函数,实际上就是执行函数体中的代码。
- # 省略function 关键字:
- name() {
- statements
- [return value]
- }
-
- # 省略函数名后面的小括号:
- function name {
- statements
- [return value]
- }
Shell 中的函数在定义时不能指明参数,但是在调用时却可以传递参数。
在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数... $10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。
特殊字符用来处理参数:
参数处理 | 说明 |
---|---|
$# | 传递到脚本或函数的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 |
$- | 显示Shell使用的当前选项,与set命令功能相同。 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
调用 Shell 函数时可以给它传递参数,也可以不传递。如果不传递参数,直接给出函数名字即可:
name
如果传递参数,那么多个参数之间以空格分隔:
name param1 param2 param3
不管是哪种形式,函数名字后面都不需要带括号。
Shell 函数在定义时不能指明参数,但是在调用时却可以传递参数,并且给它传递什么参数它就接收什么参数。