Linux基础系列文章大纲
Shell系列文章大纲


Bash测试命令

test命令或功能等价的Bash内置命令[ ]可以做条件测试,如果测试的结果为True,则退出状态码为0。

此外,还可以使用[[]]来做条件测试,它比test多一个正则匹配的功能。此外,let、$[]、$(())也可以做条件测试。但我的建议是只掌握一个测试方式,要么掌握[]测试,要么掌握[[]]。我个人是习惯于使用[],并在很偶尔做正则测试的时候用[[]],但更多正则测试我还是使用echo+grep/perl来完成的。

这些条件测试常用在if、while语句中,也常用在cmd1 && cmd2 || cmd3格式的命令行中。

用法示例:

1
2
3
sh_file=test.sh
[ -x "$sh_file" ] && ./$sh_file || { echo "can't execute,exit...";exit 1; }
test -x "$sh_file" && ./$sh_file || { echo "can't execute,exit...";exit 1; }

[][[]]中的条件测试语句都需要和开闭中括号使用空格隔开,否则语法解析错误。

无测试内容

1
2
[  ]
test

没有任何测试内容时,直接返回false。

true和false命令

true命令直接返回true,即退出状态码为0。

false命令直接返回false,即退出状态码非0。

1
2
3
4
$ true
$ echo $? # 0
$ false
$ echo $? # 1

文件类测试

条件表达式 含义
-e file 文件是否存在(exist)
-f file 文件是否存在且为普通文件(file)
-d file 文件是否存在且为目录(directory)
-b file 文件是否存在且为块设备block device
-c file 文件是否存在且为字符设备character device
-S file 文件是否存在且为套接字文件Socket
-p file 文件是否存在且为命名管道文件FIFO(pipe)
-L file 文件是否存在且是一个链接文件(Link)

测试是否是终端

1
2
3
4
5
6
7
8
9
if [ -t 1 ]; then # is terminal?
BOLD="\e[1m"; DIM="\e[2m";
RED="\e[0;31m"; RED_BOLD="\e[1;31m";
YELLOW="\e[0;33m"; YELLOW_BOLD="\e[1;33m";
GREEN="\e[0;32m"; GREEN_BOLD="\e[1;32m";
BLUE="\e[0;34m"; BLUE_BOLD="\e[1;34m";
GREY="\e[37m"; CYAN_BOLD="\e[1;36m";
RESET="\e[0m";
fi

文件属性类测试

条件表达式 含义
-r file 文件是否存在且当前用户可读
-w file 文件是否存在且当前用户可写
-x file 文件是否存在且当前用户可执行
-s file 文件是否存在且大小大于0字节,即检测文件是否非空文件
-N file 文件是否存在,且自上次read后是否被modify

两文件之间的比较

条件表达式 含义
file1 -nt file2 (newer than)判断file1是否比file2新
file1 -ot file2 (older than)判断file1是否比file2旧
file1 -ef file2 (equal file)判断file1与file2是否为同一文件

数值大小比较

条件表达式 含义
int1 -eq int2 两数值相等(equal)
int1 -ne int2 两数值不等(not equal)
int1 -gt int2 n1大于n2(greater than)
int1 -lt int2 n1小于n2(less than)
int1 -ge int2 n1大于等于n2(greater than or equal)
int1 -le int2 n1小于等于n2(less than or equal)

字符串比较

条件表达式 含义
-z str (zero)判定字符串是否为空?str为空串,则true
str
-n str
判定字符串是否非空?str为串,则false。注:-n可省略
str1 = str2
str1 == str2
str1和str2是否相同,相同则返回true。『==』和『=』等价
str1 != str2 str1是否不等于str2,若不等,则返回true
str1 > str2 str1字母顺序是否大于str2,若大于则返回true
str1 < str2 str1字母顺序是否小于str2,若小于则返回true

逻辑运算符

条件表达式 含义
-a或&& (and)两表达式同时为true时才为true。
『-a』只能在test或[]中使用,&&只能在[[]]中使用
-o或|| (or)两表达式任何一个true则为true。
『-o』只能在test或[]中使用,||只能在[[]]中使用
! 对表达式取反
( ) 改变表达式的优先级,为了防止被shell解析,应加上反斜线转义\( \)

对于逻辑与和逻辑或,下面实现的效果是等价的:

1
2
3
4
5
[ $a -gt 1 -a $a -lt 5 ]
[ $a -gt 1 ] && [ $a -lt 5 ]

[ $a -gt 5 -o $a -lt 1 ]
[ $a -gt 5 ] || [ $a -lt 1 ]

双中括号测试[[]]

双中括号也能进行测试,测试功能和[]相比只有少数不同,但它有一个非常好用的功能:正则测试,正则测试后还能获取正则匹配到的内容。

我个人通常是习惯使用[]进行测试的,只有在需要正则测试的时候才很偶尔使用[[]],大多数正则测试还是使用echo + grep/perl。

这里我介绍下[[]]的正则测试功能,大家可以根据自己的喜好来决定使用哪种方式。

[[]]中,使用=~符号开启正则测试功能,它使用的是扩展正则。

1
2
[[ "hello123world" =~ [0-9]+ ]] 
echo $? # 0

=~左边是待匹配的字符串或变量,右边是正则表达式。

正则表达式部分默认不加引号包围,如果加引号(无论是单双引号)包围,将和反斜线\功能一样,对整个正则表达式进行转义。

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 匹配成功,正则元字符点能匹配任意字符
[[ "abc.jpg" =~ .b ]]
echo $? # 0

# 引号包围的正则元字符点被转义,它只能匹配点字符,不能再匹配任意字符
# 所以,下面匹配失败
[[ "abc.jpg" =~ ".b" ]]
echo $? # 1

# 正则元字符点被反斜线转义,也只能匹配点字符
# 所以匹配失败
[[ "abc.jpg" =~ \.b ]]
echo $? # 1

例如:

1
2
3
4
5
[[ "abc1234,789" =~ [a-z]{1,}([0-9]+),([0-9]+) ]]
echo ${!BASH_REMATCH[@]} # 0 1 2
echo ${BASH_REMATCH[0]} # abc1234,789
echo ${BASH_REMATCH[1]} # 1234
echo ${BASH_REMATCH[2]} # 789

条件测试注意事项

1.无论是[]还是[[]],都建议对其内变量、字符串使用双引号包围。换句话说,能做字符串比较的时候,不要用数值比较。

例如:

1
2
name="Ma long"
[ $name = "Ma long" ]

上面的测试语句将报错,因为在变量替换阶段,$name被替换为『Ma long』,但它们没有在引号内,于是进行单词拆分,这就等价于执行的是[ Ma long = "Ma long" ],显然这是错误的语法。所以,建议加上双引号:

1
[ "$name" = "Ma long" ]

2.数值比较时,建议双方同时加0,避免变量为空时报错。

例如,变量a为空,下面的表达式是错误的。因为它被shell解析后相当于[ -eq 7 ],而这是错误的语法。

1
2
$ [ $a -eq 7 ]
-bash: [: -eq: unary operator expected

采取第一种建议,将$a使用引号包围的话,还是错的。因为被shell解析后相当于[ "" -eq 7 ],字符串和数值无法比较。注意这里的报错和上面的错误信息不一样。

1
2
$ [ "$a" -eq 7 ]
-bash: [: : integer expression expected

所以最好的方法是将它改为字符串来测试,或者双方同时加0,由于此处有一方是常量数值,所以只需为变量部分加0即可。

1
2
[ "$a" = "7" ]
[ $[a+0] -le 8 ]

3.当变量可能为空的时候,强烈建议在变量的基础上加上其他辅助字符串。

上面的语句虽然能正确测试。其实更安全的方法是采用下面的形式:

1
2
3
[ "a$a" = "a7" ]   # 判断a是否为7
[ "a$a" = "a" ] # 判断a是否为空
[ ! -z "$a" -a "a$a" = "a7" ] # a不为空且a=7时才为真

4.另外,在[][[]]中,每个地方都有空格。这不是书写建议,而是强制要求的格式。