「速通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 天前
把 Agent 做成一家公司,真比通用提示词好用吗?
python·agent·shell
寺中人2 天前
Linux 基础命令入门实战教程:从零掌握常用操作,新手快速上手
linux·运维·服务器·shell·linux 命令·linux 基础教程·linux 入门
k4m7v2pz5 天前
34 天 33 个版本:rvs(rust-verb-shell)如何用版本号对抗 AI 误判
rust·shell·命令行工具·ai agent·系统信息·跨平台开发
笨笨狗吞噬者5 天前
IRIS: shell 中的智能实时输入提示工具
github·shell
Lyra_Infra8 天前
Java 应用启动脚本 JDK 路径适配优化文档
java·shell
寒水馨10 天前
macOS下载、安装PowerShell-v7.6.4(附安装包powershell-7.6.4-osx-arm64.pkg)
macos·自动化·跨平台·shell·脚本·命令行·powershell
爱码少年11 天前
一条Shell命令实现文件目录服务器间快速转移
服务器·shell
重生的黑客13 天前
Linux 进程程序替换与自定义 Shell:从 exec 函数族到命令行解释器
linux·运维·服务器·shell
逻极16 天前
Shell脚本实战:从“能跑”到“高效”的3个关键跃升
linux·服务器·shell·脚本