type
status
date
slug
summary
tags
category
icon
password
Property
条件判断
if 结构
if
是最常用的条件判断结构,只有符合给定条件时,才会执行指定的命令:这个命令分成三个部分:
if
、elif
和else
,后两个部分是可选的。if
关键字后面是主要的判断条件,elif
用来添加在主条件不成立时的其他判断条件,else
则是所有条件都不成立时要执行的部分。if
和then
写在同一行时,需要分号分隔。分号是 Bash 的命令分隔符。它们也可以写成两行,这时不需要分号。true
和false
是两个特殊命令,前者代表操作成功,后者代表操作失败。if true
意味着命令部分总是会执行,if false
意味着命令部分永远不会执行。除了多行的写法,
if
结构也可以写成单行。注意,
if
关键字后面也可以是一条命令,该条命令执行成功(返回值0
),就意味着判断条件成立。if
后面是一条命令echo 'hi'
。该命令会执行,如果返回值是0
,则执行then
的部分。if
后面可以跟任意数量的命令。这时,所有命令都会执行,但是判断真伪只看最后一个命令,即使前面所有命令都失败,只要最后一个命令返回0
,就会执行then
的部分。if
后面有两条命令(false;true;
),第二条命令(true
)决定了then
的部分是否会执行。elif
部分可以有多个:test 命令
if
结构的判断条件,一般使用test
命令,有三种形式:expression
是一个表达式。这个表达式为真,test
命令执行成功(返回值为0
);表达式为伪,test
命令执行失败(返回值为1
)。注意,第二种和第三种写法,[
和]
与内部的表达式之间必须有空格。test
命令采用两种写法,判断/etc/hosts
文件是否存在,这两种写法是等价的。命令执行后,返回值为0
,表示该文件确实存在。实际上,
[
这个字符是test
命令的一种简写形式,可以看作是一个独立的命令,这解释了为什么它后面必须有空格。下面把
test
命令的三种形式,用在if
结构中,判断一个文件是否存在。判断表达式
if
关键字后面,跟的是一个命令。这个命令可以是test
命令,也可以是其他命令。命令的返回值为0
表示判断成立,否则表示不成立。因为这些命令主要是为了得到返回值,所以可以视为表达式。文件判断
以下表达式用来判断文件状态:
[ -a file ]
:如果 file 存在,则为true
。
[ -b file ]
:如果 file 存在并且是一个块(设备)文件,则为true
。
[ -c file ]
:如果 file 存在并且是一个字符(设备)文件,则为true
。
[ -d file ]
:如果 file 存在并且是一个目录,则为true
。
[ -e file ]
:如果 file 存在,则为true
。
[ -f file ]
:如果 file 存在并且是一个普通文件,则为true
。
[ -g file ]
:如果 file 存在并且设置了组 ID,则为true
。
[ -G file ]
:如果 file 存在并且属于有效的组 ID,则为true
。
[ -h file ]
:如果 file 存在并且是符号链接,则为true
。
[ -k file ]
:如果 file 存在并且设置了它的“sticky bit”,则为true
。
[ -L file ]
:如果 file 存在并且是一个符号链接,则为true
。
[ -N file ]
:如果 file 存在并且自上次读取后已被修改,则为true
。
[ -O file ]
:如果 file 存在并且属于有效的用户 ID,则为true
。
[ -p file ]
:如果 file 存在并且是一个命名管道,则为true
。
[ -r file ]
:如果 file 存在并且可读(当前用户有可读权限),则为true
。
[ -s file ]
:如果 file 存在且其长度大于零,则为true
。
[ -S file ]
:如果 file 存在且是一个网络 socket,则为true
。
[ -t fd ]
:如果 fd 是一个文件描述符,并且重定向到终端,则为true
。 这可以用来判断是否重定向了标准输入/输出/错误。
[ -u file ]
:如果 file 存在并且设置了 setuid 位,则为true
。
[ -w file ]
:如果 file 存在并且可写(当前用户拥有可写权限),则为true
。
[ -x file ]
:如果 file 存在并且可执行(有效用户有执行/搜索权限),则为true
。
[ FILE1 -nt FILE2 ]
:如果 FILE1 比 FILE2 的更新时间更近,或者 FILE1 存在而 FILE2 不存在,则为true
。
[ FILE1 -ot FILE2 ]
:如果 FILE1 比 FILE2 的更新时间更旧,或者 FILE2 存在而 FILE1 不存在,则为true
。
[ FILE1 -ef FILE2 ]
:如果 FILE1 和 FILE2 引用相同的设备和 inode 编号,则为true
。
$FILE
要放在双引号之中,这样可以防止变量$FILE
为空,从而出错。因为$FILE
如果为空,这时[ -e $FILE ]
就变成[ -e ]
,这会被判断为真。而$FILE
放在双引号之中,[ -e "$FILE" ]
就变成[ -e "" ]
,这会被判断为伪。字符串判断
[ string ]
:如果string
不为空(长度大于0),则判断为真。
[ -n string ]
:如果字符串string
的长度大于零,则判断为真。
[ -z string ]
:如果字符串string
的长度为零,则判断为真。
[ string1 = string2 ]
:如果string1
和string2
相同,则判断为真。
[ string1 == string2 ]
等同于[ string1 = string2 ]
。
[ string1 != string2 ]
:如果string1
和string2
不相同,则判断为真。
[ string1 '>' string2 ]
:如果按照字典顺序string1
排列在string2
之后,则判断为真。
[ string1 '<' string2 ]
:如果按照字典顺序string1
排列在string2
之前,则判断为真。
注意,
test
命令内部的>
和<
,必须用引号引起来(或者是用反斜杠转义)。否则,它们会被 shell 解释为重定向操作符。首先确定
$ANSWER
字符串是否为空。如果为空,就终止脚本,并把退出状态设为1
。注意,这里的echo
命令把错误信息There is no answer.
重定向到标准错误,这是处理错误信息的常用方法。如果$ANSWER
字符串不为空,就判断它的值是否等于yes
、no
或者maybe
。字符串判断时,变量要放在双引号之中,比如
[ -n "$COUNT" ]
,否则变量替换成字符串以后,test
命令可能会报错,提示参数过多。另外,如果不放在双引号之中,变量为空时,命令会变成[ -n ]
,这时会判断为真。如果放在双引号之中,[ -n "" ]
就判断为伪。整数判断
[ integer1 -eq integer2 ]
:如果integer1
等于integer2
,则为true
。
[ integer1 -ne integer2 ]
:如果integer1
不等于integer2
,则为true
。
[ integer1 -le integer2 ]
:如果integer1
小于或等于integer2
,则为true
。
[ integer1 -lt integer2 ]
:如果integer1
小于integer2
,则为true
。
[ integer1 -ge integer2 ]
:如果integer1
大于或等于integer2
,则为true
。
[ integer1 -gt integer2 ]
:如果integer1
大于integer2
,则为true
。
先判断变量
$INT
是否为空,然后判断是否为0
,接着判断正负,最后通过求余数判断奇偶。正则判断
[[ expression ]]
这种判断形式,支持正则表达式。先判断变量
INT
的字符串形式,是否满足^-?[0-9]+$
的正则模式,如果满足就表明它是一个整数。test 判断的逻辑运算
通过逻辑运算,可以把多个
test
判断表达式结合起来,创造更复杂的判断。三种逻辑运算AND
,OR
,和NOT
,都有自己的专用符号。AND
运算:符号&&
,也可使用参数a
OR
运算:符号||
,也可使用参数o
NOT
运算:符号!
一个
AND
的例子,判断整数是否在某个范围之内。上面例子中,
&&
用来连接两个判断条件:大于等于$MIN_VAL
,并且小于等于$MAX_VAL
。使用否定操作符
!
时,最好用圆括号确定转义的范围。上面例子中,
test
命令内部使用的圆括号,必须使用引号或者转义,否则会被 Bash 解释。算术判断
Bash 还提供了
((...))
作为算术条件,进行算术运算的判断。算术判断不需要使用
test
命令,而是直接使用((...))
结构。这个结构的返回值,决定了判断的真伪。如果算术计算的结果是非零值,则表示判断成立。这一点跟命令的返回值正好相反,需要小心。算术条件
((...))
也可以用于变量赋值。(( foo = 5 ))
完成了两件事情。首先把5
赋值给变量foo
,然后根据返回值5
,判断条件为真。注意,赋值语句返回等号右边的值,如果返回的是
0
,则判断为假。用算术条件改写的数值判断脚本。
只要是算术表达式,都能用于
((...))
语法。普通命令的逻辑运算
如果
if
结构使用的不是test
命令,而是普通命令,比如((...))
算术运算,或者test
命令与普通命令混用,那么可以使用 Bash 的命令控制操作符&&
(AND)和||
(OR),进行多个命令的逻辑运算。对于
&&
操作符,先执行command1
,只有command1
执行成功后, 才会执行command2
。对于||
操作符,先执行command1
,只有command1
执行失败后, 才会执行command2
。if
与&&
结合使用的写法只有在指定文件里面,同时存在搜索词
word1
和word2
,就会执行if
的命令部分:将一个
&&
判断表达式,改写成对应的if
结构:case 结构
case
结构用于多值判断,可以为每个值指定对应的命令,跟包含多个elif
的if
结构等价,但是语义更好:expression
是一个表达式,pattern
是表达式的值或者一个模式,可以有多条,用来匹配多个值,每条以两个分号(;
)结尾。最后一条匹配语句的模式是
*
,这个通配符可以匹配其他字符和没有输入字符的情况,类似if
的else
部分。判断当前是什么操作系统:
case
的匹配模式可以使用各种通配符:a)
:匹配a
。
a|b)
:匹配a
或b
。
[[:alpha:]])
:匹配单个字母。
???)
:匹配3个字符的单词。
.txt)
:匹配.txt
结尾。
)
:匹配任意输入,通过作为case
结构的最后一个模式。
Bash 4.0之前,
case
结构只能匹配一个条件,然后就会退出case
结构。Bash 4.0之后,允许匹配多个条件,这时可以用;;&
终止每个条件块。执行上面的脚本,会得到下面的结果。
可以看到条件语句结尾添加了
;;&
以后,在匹配一个条件之后,并没有退出case
结构,而是继续判断下一个条件。循环
while 循环
while
循环有一个判断条件,只要符合条件,就不断循环执行指定的语句:只要满足条件
condition
,就会执行命令commands
。然后,再次判断是否满足条件condition
,只要满足,就会一直执行下去。只有不满足条件,才会退出循环。循环条件
condition
可以使用test
命令,跟if
结构的判断条件写法一致。关键字
do
可以跟while
不在同一行,这时两者之间不需要使用分号分隔。上面的例子会无限循环,可以按下 Ctrl + c 停止。
while
循环写成一行,也是可以的。while
的条件部分也可以是执行一个命令:while
的条件部分可以执行任意数量的命令,但是执行结果的真伪只看最后一个命令的执行结果。上面代码运行后,不会有任何输出,因为
while
的最后一个命令是false
。until 循环
until
循环与while
循环恰好相反,只要不符合判断条件(判断条件失败),就不断循环执行指定的语句。一旦符合判断条件,就退出循环。关键字
do
可以与until
不写在同一行,这时两者之间不需要分号分隔。until
的条件部分也可以是一个命令,表示在这个命令执行成功之前,不断重复尝试。一般来说,
until
用得比较少,完全可以统一都使用while
。until
循环都可以转为while
循环,只要把条件设为否定即可:for...in 循环
for...in
循环用于遍历列表的每一项。关键词
do
可以跟for
写在同一行,两者使用分号分隔。word1 word2 word3
是一个包含三个单词的列表,变量i
依次等于word1
、word2
、word3
,命令echo $i
则会相应地执行三次。列表可以由通配符产生:
列表也可以通过子命令产生:
cat ~/.bash_profile
命令会输出~/.bash_profile
文件的内容,然后通过遍历每一个词,计算该文件一共包含多少个词,以及每个词有多少个字符。in list
的部分可以省略,这时list
默认等于脚本的所有参数$@
。但是,为了可读性,最好还是不要省略。在函数体中也是一样的,
for...in
循环省略in list
的部分,则list
默认等于函数的所有参数。for 循环
for
循环还支持 C 语言的循环语法。expression1
用来初始化循环条件,expression2
用来决定循环结束的条件,expression3
在每次循环迭代的末尾执行,用于更新值。注意,循环条件放在双重圆括号之中。另外,圆括号之中使用变量,不必加上美元符号
$
。它等同于下面的
while
循环。下面是一个例子。
for
条件部分的三个语句,都可以省略。上面脚本会反复读取命令行输入,直到用户输入了一个点(
.
)为止,才会跳出循环。break,continue
Bash 提供了两个内部命令
break
和continue
,用来在循环内部跳出循环。break
命令立即终止循环,程序继续执行循环块之后的语句,即不再执行剩下的循环。只会打印3行结果。一旦变量
$number
等于3,就会跳出循环,不再继续执行。continue
命令立即终止本轮循环,开始执行下一轮循环。只要用户输入的文件不存在,
continue
命令就会生效,直接进入下一轮循环(让用户重新输入文件名),不再执行后面的打印语句。select 结构
select
结构主要用来生成简单的菜单。它的语法与for...in
循环基本一致。Bash 会对
select
依次进行下面的处理:select
生成一个菜单,内容是列表list
的每一项,并且每一项前面还有一个数字编号。
- Bash 提示用户选择一项,输入它的编号。
- 用户输入以后,Bash 会将该项的内容存在变量
name
,该项的编号存入环境变量REPLY
。如果用户没有输入,就按回车键,Bash 会重新输出菜单,让用户选择。
- 执行命令体
commands
。
- 执行结束后,回到第一步,重复这个过程。
执行上面的脚本,Bash 会输出一个品牌的列表,让用户选择:
如果用户没有输入编号,直接按回车键。Bash 就会重新输出一遍这个菜单,直到用户按下
Ctrl + c
,退出执行。select
可以与case
结合,针对不同项,执行不同的命令。