尧图精选

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

🕒 发布时间:2026/10/2 13:58:04 📁 来源:尧图网络
Shell条件判断与运算符test、[ ]、、if/case实战脚本里光按顺序执行命令远远不够得能根据情况分叉文件存在就备份磁盘超 80% 就告警参数是-v就打印详细日志。这些都靠条件判断。这一篇把if、test、[ ]、[[ ]]和case一次讲透重点是几组运算符的真实区别和最容易写错的空格。一、if 语句的基本结构if [ 条件 ]; then 命令 elif [ 条件 ]; then 命令 else 命令 fi注意三个语法点if后面那一行必须以then结尾同一行写就用分号隔开整段以fi收尾条件部分和方括号之间必须有空格。#!/bin/bash score85 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 debugfalse $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 userguest fi echo 当前用户: $user预期输出user 是空的用默认值 guest 当前用户: guest数值比较数值比较不能用因为那两个符号在 shell 里是重定向。数值比较用专用字母写法含义对应数学符号-eq等于-ne不等!-lt小于-le小于等于≤-gt大于-ge大于等于≥#!/bin/bash used$(df / | awk NR2{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 filenameaccess.log if [[ $filename *.log ]]; then echo $filename 是日志文件 fi ip192.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 脚本能用[[ ]]就用它少写很多引号。六、逻辑组合!、、||条件之间要与或非有两种写法。第一种在[ ]内部用-aand和-oorif [ -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再比较数字干净得多。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →