上一篇我们了解了Shell变量的引用,能够将变量融入到shell语句中,并进行相关操作,今天将讲述Shell编程的条件测试,根据不同的判断条件将语句组合起来,去选择不同的路径运行,帮助我们自动完成不同的工作。
概念
Shell 条件测试是 Shell 脚本中常用的一种语法,用于在脚本中进行条件判断。Shell 条件测试主要包括变量测试、文件测试、字符串测试、数值测试、逻辑测试等。其中,变量测试用于判断变量的值是否为空或者存在,文件测试用于判断文件或目录是否存在或者可读可写可执行,字符串测试用于判断两个字符串是否相等,数值测试用于判断两个数字是否相等、大于或小于,逻辑测试用于组合多个条件。
在 Shell 中,条件测试有两种写法:
bash
# 写法一:test 命令
test 条件表达式
# 写法二:中括号(更常用,注意两端必须有空格)
[ 条件表达式 ]
Shell 通过 $? 来判断条件是否成立:返回 0 表示条件成立(真),返回非 0 表示条件不成立(假) 。下面我们逐一来学习。
1 变量测试
变量测试主要用于判断变量的值是否为空或者存在。Shell 中用于变量测试的操作符包括 -z 和 -n,其中 -z 表示判断变量是否为空,-n 表示判断变量是否不为空(是否存在)。
| 操作符 | 作用 |
|---|---|
-z |
判断变量是否为空(长度为 0) |
-n |
判断变量是否不为空(长度非 0) |
= |
判断两个变量/字符串是否相等 |
!= |
判断两个变量/字符串是否不相等 |
常用写法:
bash
echo $VARIABLE_NAME # 打印变量的值
echo ${VARIABLE_NAME} # 打印变量的值
echo "Value is: $VARIABLE_NAME" # 在字符串中使用变量
条件判断写法:
bash
if [ -z "$VARIABLE_NAME" ]; then echo "Variable is empty"; fi # 检查变量是否为空
if [ -n "$VARIABLE_NAME" ]; then echo "Variable is not empty"; fi # 检查变量是否不为空
if [ "$VARIABLE_NAME" = "some_value" ]; then echo "Variable is some_value"; fi # 检查变量是否等于某个值
if [ "$VARIABLE_NAME" != "some_value" ]; then echo "Variable is not some_value"; fi # 检查变量是否不等于某个值
完整示例:
bash
if [ -z "$var" ]; then
echo "变量为空"
else
echo "变量不为空"
fi
在上面的示例中,如果变量 $var 为空,则输出 "变量为空",否则输出 "变量不为空"。
使用 -z 或 -n 测试变量时,变量建议加上双引号,避免因为变量为空或包含空格时产生歧义,导致判断结果有误。
sql
[root@localhost ~]# empty=""
[root@localhost ~]# [ -z $empty ]; echo $?
0
[root@localhost ~]# [ -n $empty ]; echo $? # 变量没加引号,导致判断结果有误
0
[root@localhost ~]# [ -n "$empty" ]; echo $? # 加上引号,判断结果正确
1
2 字符串测试
字符串测试主要用于判断两个字符串是否相等、是否为空。Shell 中用于字符串测试的操作符包括 =、!=、-z、-n,其中 = 表示判断两个字符串是否相等,!= 表示判断两个字符串是否不相等。
bash
if [ "$str1" = "$str2" ]; then
echo "字符串相等"
else
echo "字符串不相等"
fi
在上面的示例中,如果字符串 $str1 和 $str2 相等,则输出 "字符串相等",否则输出 "字符串不相等"。
2.1 单括号 \[\] vs 双括号 \[ ]
在 shell 中,可以使用方括号 [] 或双括号 [[ ]] 来进行字符串比较。但是,使用双括号 [[ ]] 进行字符串比较比使用方括号 [] 更加灵活和强大,原因主要有以下三点:
(1) 支持更多比较运算符
双括号 [[ ]] 支持更多的比较运算符,例如 <、<=、>、>= 和 !=,而方括号 [] 仅支持 = 和 != 这两个比较运算符。如果您想比较两个字符串的大小,那么只能使用双括号 [[ ]],不能使用方括号 []。
bash
if [[ "$STRING1" < "$STRING2" ]]; then
echo "$STRING1 is less than $STRING2"
else
echo "$STRING1 is greater than or equal to $STRING2"
fi
(2) 不需要强制引用变量
在使用双括号 [[ ]] 进行字符串比较时,可以不用严格引用变量,即不需要将变量名放在引号内。这是因为双括号 [[ ]] 可以自动识别变量,并将其作为字符串处理。这样可以使代码更加简洁,减少引号的使用。
bash
if [[ $STRING1 = $STRING2 ]]; then
echo "Strings are equal"
else
echo "Strings are not equal"
fi
在上述示例中,$STRING1 和 $STRING2 都没有使用引号,但仍能正确比较。
(3) 支持原生逻辑运算符
双括号 [[ ]] 支持 && 和 || 作为逻辑运算符,可以用于将多个条件组合在一起,代码更易读;而单括号 [] 中只能使用 -a、-o 或者多个 [ ] 拼接。
bash
if [[ -n $STRING1 && -n $STRING2 ]]; then
echo "Both strings are not empty"
else
echo "One or both strings are empty"
fi
使用双括号 [[ ]] 进行字符串比较比使用方括号 [] 更加灵活和强大。当需要进行字符串比较(尤其是需要比较大小、组合多个条件)时,建议使用双括号 [[ ]]。但是需要注意的是,双括号 [[ ]] 只能在 bash、zsh、ksh 等高级 shell 中使用,在 sh 等其他 shell 中可能不被支持,脚本头部需要 #!/bin/bash。
3 文件测试
文件测试主要用于判断文件或目录是否存在或者可读可写可执行。Shell 中常用的文件测试操作符包括 -e、-f、-d、-r、-w、-x 等。
3.1 常用操作符
| 操作符 | 作用 |
|---|---|
-e |
判断文件或目录是否存在(Exist) |
-f |
判断是否为普通文件(File) |
-d |
判断是否为目录文件(Directory) |
-r |
判断当前用户是否可读(Read) |
-w |
判断当前用户是否可写(Write) |
-x |
判断当前用户是否可执行(eXcute) |
-L |
判断是否为符号链接文件(软链接) |
-s |
判断文件是否存在且非空 |
file1 -nt file2 |
判断 file1 是否比 file2 新(Newer Than) |
file1 -ot file2 |
判断 file1 是否比 file2 旧(Older Than) |
file1 -ef file2 |
判断两个文件是否为同一文件(指向同一个 inode) |
注意: 测试文件是否存在时,建议使用 -e,! -e 取反时比 -a 更准确。
3.2 基础示例
bash
if [ -e "/path/to/file_or_dir" ]; then
echo "文件或目录存在"
else
echo "文件或目录不存在"
fi
在上面的示例中,如果文件或目录 "/path/to/file_or_dir" 存在,则输出 "文件或目录存在",否则输出 "文件或目录不存在"。
3.3 实战示例
csharp
# 测试是否为目录
[root@localhost ~]# [ -d /etc/ ]; echo $?
0
# 测试是否为普通文件
[root@localhost ~]# [ -f /etc/sysconfig ]; echo $?
1
# 测试文件是否存在
[root@localhost ~]# [ -e /etc/passwd ]; echo $?
0
# 测试 root 用户对文件是否拥有读、写、执行权限
# 注意:root 用户的读取和写入权限,不受文件的基本权限控制;但执行权限受文件基本权限控制
[root@localhost ~]# ll /etc/shadow
- 1 root root 3792 3月 31 21:59 /etc/shadow
[root@localhost ~]# [ -r /etc/shadow ]; echo $?
0
[root@localhost ~]# [ -w /etc/shadow ]; echo $?
0
[root@localhost ~]# [ -x /etc/shadow ]; echo $?
1
注意,测试软链接时,目录名称后面不要加 "/" ,不然会被判断为目录文件。
csharp
[root@localhost /]# ls -dl /bin
lrwxrwxrwx. 1 root root 7 3月 8 19:13 /bin -> usr/bin # 该文件为软链接文件
[root@localhost /]# [ -L /bin ]; echo $? # 测试是否为软链接,是
0
[root@localhost /]# [ -L /bin/ ]; echo $? # 加上 /,被判断为目录文件
1
4 数值测试
数值测试用于判断两个整数的大小关系。Shell 中用于数值测试的操作符包括 -eq、-ne、-gt、-lt、-ge、-le。
| 操作符 | 含义 |
|---|---|
-eq |
等于(Equal) |
-ne |
不等于(Not Equal) |
-gt |
大于(Greater Than) |
-lt |
小于(Lesser Than) |
-le |
小于或等于(Lesser or Equal) |
-ge |
大于或等于(Greater or Equal) |
数值测试只能用于整数 ,不能直接用于浮点数;且等号和不等于必须是 -eq 和 -ne ,而不是 = 和 !=(后者是字符串比较)。
基础示例:
css
[root@localhost ~]# [ 2 -lt 3 ]; echo $? # 测试 2 是否小于 3
0
[root@localhost ~]# a=5
[root@localhost ~]# b=6
[root@localhost ~]# [ $a -eq $b ]; echo $? # 测试变量 a 是否等于变量 b
1
[root@localhost ~]# [ $a -lt $b ]; echo $? # 测试 a 是否小于 b
0
实战示例: 查询当前目录下的文件数量是否大于 10,如果大于则进行提示。
csharp
[root@localhost ~]# ls | wc -l
40
[root@localhost ~]# test `ls | wc -l` -gt 10 && echo "文件数量大于10"
文件数量大于10
5 逻辑测试
逻辑测试用于连接多个测试条件,并返回整个表达式的值。逻辑测试主要有 "逻辑与"、"逻辑或"、"逻辑否" 三种。
5.1 逻辑运算符
| 在 \[\] 中 | 在 \[ ] 中 | 含义 |
|---|---|---|
-a |
&& |
逻辑与,全真才为真 |
-o |
` | |
! |
! |
逻辑否 |
两种格式:
lua
# 格式一:中括号拼接
[ 表达式1 ] && [ 表达式2 ] # 等同于 [ 表达式1 -a 表达式2 ] 等同于 [[ 表达式1 && 表达式2 ]]
[ 表达式1 ] || [ 表达式2 ] # 等同于 [ 表达式1 -o 表达式2 ] 等同于 [[ 表达式1 || 表达式2 ]]
# 格式二:命令拼接
命令1 && 命令2
命令1 || 命令2
5.2 短路与 &&
CMD1 && CMD2:全真才为真,一假即为假。
- 第一个
CMD1结果为真,第二个CMD2必须要参与运算,才能得到最终的结果。 - 第一个
CMD1结果为假,总的结果必定为假,因此不需要执行CMD2。
5.3 短路或 ||
CMD1 || CMD2:一真即为真。
- 第一个
CMD1结果为真,总的结果必定为真,因此不需要执行CMD2。 - 第一个
CMD1结果为假,第二个CMD2必须要参与运算,才能得到最终的结果。
5.4 实战示例
示例一:简单的条件组合
java
# 一个命令正确执行,输出 yes,否则输出 no
[root@localhost ~]# [ -e student.txt ] && echo 'yes' || echo 'no'
yes
[root@localhost ~]# [ -e sss ] && echo 'yes' || echo 'no'
no
示例二:多条件组合
swift
[root@localhost ~]# a=5
[root@localhost ~]# [ $a -ne 1 ] && [ $a != 2 ]; echo $?
0
[root@localhost ~]# [ $a -ne 1 -a $a != 2 ]; echo $?
0
[root@localhost ~]# [[ $a -ne 1 && $a != 2 ]]; echo $?
0
示例三:字符串与逻辑组合
kotlin
[root@localhost ~]# a=hello
[root@localhost ~]# [ -n "$a" -a "$a" == "hello" ] && echo 'yes' || echo 'no'
yes
[root@localhost ~]# [ -n "$a" -a "$a" == "world" ] && echo 'yes' || echo 'no'
no
6 综合实战:判断主机是否在线
把上面学到的内容组合起来,实现一个检测指定主机是否在线的小脚本。
方法一:使用三元运算符(基于短路与或)
perl
[root@localhost ~]# vim ping001.sh
#!/bin/bash
ping -c 3 -i 0.5 -w 3 $1 &> /dev/null && echo "$1 is up" || echo "$1 is down"
[root@localhost ~]# bash ping001.sh 192.168.72.10
192.168.72.10 is up
[root@localhost ~]# bash ping001.sh 192.168.72.22
192.168.72.22 is down
方法二:使用 if 双分支结构
bash
[root@localhost ~]# vim ping002.sh
#!/bin/bash
ping -c 3 -i 0.5 -w 3 $1 &> /dev/null
if [ $? -eq 0 ]
then
echo "$1 is online"
else
echo "$1 is offline"
fi
[root@localhost ~]# bash ping002.sh 192.168.72.10
192.168.72.10 is online
小贴士:
ping -c:发送包的个数。ping -i:发送的间隔时间。ping -w(小写):多少秒后停止 ping 操作。ping -W(大写):以毫秒为单位设置 ping 的超时时间。
7 总结
通过本篇文章,我们了解了shell的条件测试和判断,熟悉了变量测试、文件测试、字符串测试、数值测试和逻辑测试5种测试场景。在编程中,需要根据需求选择对应脚本语句,帮我们去自动完成不同的任务。
如果这篇文章对您有帮助,欢迎点赞、评论、收藏。下一篇我们将进入shell循环与遍历。