☰
Shell条件判断与运算符:test、[ ]、、if/case实战
2026/10/2 13:58:02 网站建设 项目流程

Shell条件判断与运算符:test、[ ]、、if/case实战

脚本里光按顺序执行命令远远不够,得能根据情况分叉:文件存在就备份,磁盘超 80% 就告警,参数是-v就打印详细日志。这些都靠条件判断。这一篇把if、test、[ ]、[[ ]]和case一次讲透,重点是几组运算符的真实区别和最容易写错的空格。

一、if 语句的基本结构

if [ 条件 ]; then 命令 elif [ 条件 ]; then 命令 else 命令 fi

注意三个语法点:if后面那一行必须以then结尾(同一行写就用分号隔开);整段以fi收尾;条件部分和方括号之间必须有空格。

#!/bin/bash score=85 if [ $score -ge 90 ]; then echo "优秀" elif [ $score -ge 60 ]; then echo "及格" else echo "挂科" fi

预期输出:

及格

二、test 命令与 [ ]:它们其实是同一个东西

[是一个真实存在的命令(在/usr/bin/[,bash 也有内置版本),它的任务是"判断条件,结果成立返回 0,不成立返回 1"。test命令和它等价,只是写法不同。

test -f /etc/hostname && echo "文件存在" [ -f /etc/hostname ] && echo "文件存在"

这两行效果完全一样。方括号末尾的]只是[命令要求的最后一个参数,纯粹是为了语法对称。

⚠️ 空格是硬要求

[ -f /etc/hostname ] # 正确 [-f /etc/hostname] # 错误:shell 把 [-f 当成一个命令名 [ -f /etc/hostname] # 错误:] 前面没空格 [ -f "/etc/hostname" ] # 正确

写成[-f /etc/hostname],shell 会去找一个叫[-f的可执行文件,报command not found。这是新手写if第一周最高频的报错。

三、文件测试运算符

判断文件状态是脚本里用得最多的一类条件。

运算符判断为真的条件
-e 路径文件或目录存在
-f 路径存在且是普通文件
-d 路径存在且是目录
-r 路径当前用户可读
-w 路径可写
-x 路径可执行
-s 路径存在且大小非 0
-L 路径是符号链接

完整示例:写一个脚本检查配置文件,不存在就自动建一个。

#!/bin/bash config="$HOME/myapp.conf" if [ ! -f "$config" ]; then echo "配置文件不存在,创建默认配置" echo "debug=false" > "$config" else echo "配置文件已存在,跳过" fi ls -l "$config"

第一次运行预期输出:

配置文件不存在,创建默认配置 -rw-r--r-- 1 user user 12 Sep 16 10:00 /home/user/myapp.conf

再跑一次:

配置文件已存在,跳过 -rw-r--r-- 1 user user 12 Sep 16 10:00 /home/user/myapp.conf

!是逻辑非,放在条件前面,注意它和[之间也要有空格。

四、字符串与数值比较

字符串比较

写法含义
[ "$a" = "$b" ]两个字符串相等
[ "$a" != "$b" ]不相等
[ -z "$s" ]字符串为空(长度为 0)
[ -n "$s" ]字符串非空
#!/bin/bash user="" if [ -z "$user" ]; then echo "user 是空的,用默认值 guest" user="guest" fi echo "当前用户: $user"

预期输出:

user 是空的,用默认值 guest 当前用户: guest

数值比较

数值比较不能用><,因为那两个符号在 shell 里是重定向。数值比较用专用字母:

写法含义对应数学符号
-eq等于==
-ne不等!=
-lt小于<
-le小于等于≤
-gt大于>
-ge大于等于≥
#!/bin/bash used=$(df / | awk 'NR==2{print $5}' | tr -d '%') echo "根分区已用 $used%" if [ "$used" -ge 85 ]; then echo "告警:磁盘使用率超过 85%" fi

磁盘用了 72% 时预期输出:

根分区已用 72%

如果是 90%:

根分区已用 90% 告警:磁盘使用率超过 85%

⚠️ 变量为空时的坑

[ $num -ge 85 ]这种写法在num为空时会报错:shell 把它展开成[ -ge 85 ],[收到的参数不够。防御性写法是给变量补默认值:

used=${used:-0} if [ "$used" -ge 85 ]; then

${used:-0}的意思是:used没定义或为空就用 0,这是写生产脚本的标准习惯。

五、:增强版条件判断

[[ ]]是 bash 对[ ]的扩展,比 POSIX 标准的[ ]更安全、功能更多。

对比项[ ][[ ]]
变量未加引号容易因空格分词出错安全,不分词
逻辑与用-a,易出错直接用&&
逻辑或用-o直接用\|\|
正则匹配不支持[[ $str =~ ^[0-9]+$ ]]
通配符匹配不支持[[ $file == *.log ]]
可移植性所有 shell 都支持仅 bash/zsh/ksh
#!/bin/bash filename="access.log" if [[ $filename == *.log ]]; then echo "$filename 是日志文件" fi ip="192.168.1.100" if [[ $ip =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then echo "$ip 格式像一个 IPv4 地址" fi

预期输出:

access.log 是日志文件 192.168.1.100 格式像一个 IPv4 地址

什么时候用哪个?写要兼容sh的脚本(比如 Docker 里的/bin/sh)只能用[ ];自己机器上跑的 bash 脚本,能用[[ ]]就用它,少写很多引号。

六、逻辑组合:!、&&、||

条件之间要"与或非",有两种写法。

第一种,在[ ]内部用-a(and)和-o(or):

if [ -f "$file" -a -x "$file" ]; then echo "$file 存在且可执行" fi

第二种,在两个[ ]外面用&&和||(更推荐):

if [ -f "$file" ] && [ -x "$file" ]; then echo "$file 存在且可执行" fi

第二种在[[ ]]里还能写进同一对方括号:

if [[ -f $file && -x $file ]]; then echo "$file 存在且可执行" fi

&&和||还有一种"短路"用法,把它写在命令行里省掉整个 if:

[ -d /tmp/cache ] && echo "缓存目录存在" mkdir -p /tmp/cache || echo "建目录失败"

第一条:[ ]成立才执行后面的 echo。第二条:mkdir失败才执行 echo。这种写法适合一行搞定的简单分支,复杂逻辑还是老实写if。

七、case 语句:多分支的更优解

分支超过三个,写一串if elif elif会很难读。case就是为这种场景准备的。

case 变量 in 模式1) 命令 ;; 模式2) 命令 ;; *) 其他情况 ;; esac

每个分支以;;结束(相当于其他语言的break),*是兜底分支。模式支持通配符:

#!/bin/bash case "$1" in start) echo "启动服务..." ;; stop) echo "停止服务..." ;; restart) echo "重启服务..." ;; status) echo "查看状态..." ;; "") echo "用法: $0 {start|stop|restart|status}" exit 1 ;; *) echo "未知动作: $1" echo "用法: $0 {start|stop|restart|status}" exit 1 ;; esac

执行几次看效果:

./service.sh start ./service.sh status ./service.sh ./service.sh reload

预期输出依次为:

启动服务... 查看状态... 用法: ./service.sh {start|stop|restart|status} 未知动作: reload 用法: ./service.sh {start|stop|restart|status}

case的模式还支持|分隔多个值:

case "$ext" in jpg|jpeg|png|gif) echo "图片文件" ;; txt|md) echo "文本文件" ;; esac

八、知识扩展:[到底是命令还是语法

很多人以为[是 shell 的语法关键字,其实不是。它是一个外部命令,只是 bash 又内置了一份同名实现用来提速。

用type命令能看出来:

type [ type test

预期输出:

[ is a shell builtin test is a shell builtin

[本质上就是test,唯一区别是[要求最后一个参数必须是]。你甚至可以调用/usr/bin/[这个二进制文件来验证:

/usr/bin/[ -f /etc/hostname ] && echo ok

这解释了为什么[ ]对空格这么敏感——它是普通命令,参数必须用空格分开,和ls -l /tmp没区别。而[[ ]]才是真正的 shell 语法,解析由 bash 自己完成,所以它不用在意分词,还支持&&、=~这些高级特性。

另一个值得知道的细节:if本身不判断真假,它只是执行if后面那整条命令,看它的退出码。[ ... ]返回 0 表示条件成立,if就走 then 分支。理解这一点,你就能写出更灵活的判断:

if grep -q "error" /var/log/syslog; then echo "日志里有 error" fi

这里根本没用[ ],而是直接用grep -q的退出码做判断——找到了返回 0,走 then 分支。这种写法比先grep | wc -l再比较数字干净得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询