「速通Shell」织线为面,Shell条件测试和判断

上一篇我们了解了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循环与遍历。

相关推荐
吴声子夜歌2 天前
Shell编程实例——编写安全的shell脚本(二)
linux·运维·shell
吴声子夜歌4 天前
Shell编程实例——内务及管理任务(一)
linux·运维·shell
吴声子夜歌4 天前
Shell编程实例——高级脚本编程(一)
linux·运维·网络·shell
吴声子夜歌4 天前
Shell编程实例——bash的配置与自定义(二)
linux·运维·shell
吴声子夜歌5 天前
Shell编程实例——与解析相关的任务(二)
linux·运维·shell
吴声子夜歌5 天前
Shell编程实例——脚本编程的附加特性
linux·运维·shell
云计算练习生5 天前
什么是内核?操作系统内核到底管哪些事
linux·windows·操作系统·内核·shell
吴声子夜歌8 天前
Shell编程实例——bash入门
linux·运维·shell
Swizard8 天前
Linux 日志神器 journalctl 完全指南:彻底替代传统日志查看方式
shell
吴声子夜歌9 天前
Shell编程——数组
linux·运维·shell