当前位置: 首页 > ai >正文

【Day 40】Shell脚本-条件判断

一、条件判断

(一)条件判断的核心类型

Shell 中最常用的两种条件判断结构为:

  • if 条件判断:适用于 “范围判断”和 “多分支逻辑”
  • case 条件判断:适用于 “等值判断”

(二)if 条件判断

if 判断通过 “条件是否成立” 决定执行分支,支持 “单分支”“双分支”“多分支” 三种结构。

1、 基础语法

1.1 单分支 if

//(满足条件才执行)适用于 “仅当条件为真时执行操作,条件为假时无动作” 的场景,语法:

if 条件; then  # 分号“;”用于分隔“条件”和“then”(也可将 then 换行写)执行操作1  # 条件为真时执行执行操作2
fi  # 结束 if 判断(必须闭合)
1.2 双分支 if

//(满足 / 不满足条件分别执行)适用于 “条件为真执行 A 操作,条件为假执行 B 操作” 的场景

if 条件; then执行操作1  # 条件为真
else执行操作2  # 条件为假
fi
1.3 多分支 if

//(多个条件依次判断一直到成立为止)适用于 “有 3 个及以上分支” 的场景

if 条件1; then执行操作1  # 条件1为真
elif 条件2; then  # else if 的缩写,可多个执行操作2  # 条件1假、条件2真
elif 条件3; then执行操作3  # 条件1、2假,条件3真
else执行操作4  # 所有条件均假
fi

2、 if 条件的写法

if 中的 “条件” 是判断的核心,本质是一个 “命令”,if 会根据该命令的 “退出状态码” 判断分支。

  • 若命令退出状态码为 0(表示成功),则进入 then 分支;
  • 若状态码为非 0(表示失败),则跳过 then 分支(或进入 else 分支)。

因此,if 后面可以接任何 “能产生退出状态码的内容”,包括:

  1. 用 [] 或 [[ ]] 包裹的条件表达式;
  2. 直接执行的系统命令(如 id、grep 等);
  • 当条件是 “表达式”(如 a > b、“文件是否存在”)时,需要用 [] 或 [[ ]] 这类 “表达式解析工具” 来识别,否则 Shell 无法理解;
  • 当条件是 “命令”(如 id、grep)时,命令本身就能产生退出状态码,无需额外工具解析,直接写命令即可。
    2.1 基于 “命令执行结果” 

    Shell 中,命令执行成功(退出状态码 $?=0)则条件为真,失败则为假,无需额外判断符号,直接写命令即可。
    场景:判断命令是否执行成功(如文件是否能读取、服务是否能连接)。

    示例:判断 MySQL 是否能正常连接

    #!/bin/bash
    # 用 mysql 命令测试连接(-e "select 1" 执行简单查询,2> /dev/null 隐藏错误)
    if mysql -uroot -p123456 -e "select 1" 2> /dev/null; thenecho "MySQL 连接正常"
    elseecho "MySQL 连接失败,请检查密码或服务状态"
    fi
    
    2.2 基于 “数值” 

    用于 “数值大小比较”(如磁盘使用率是否超过阈值、年龄是否达标)。需用 [ 数值表达式 ] 包裹,表达式需严格遵循 “空格规则”(括号内外、运算符前后必须有空格)。

    数值表达式含义示例(判断 $a 是否大于 10)
    $a -eq $b等于(==)[ $a -eq 10 ]
    $a -ne $b不等于(!=)[ $a -ne 10 ]
    $a -gt $b大于(>)[ $a -gt 10 ]
    $a -ge $b大于等于(>=)[ $a -ge 10 ]
    $a -lt $b小于(<)[ $a -lt 10 ]
    $a -le $b小于等于(<=)[ $a -le 10 ]
    2.3 基于 “字符” 

    用于 “字符串是否相等、是否为空” 的判断,同样需用 [ 字符表达式 ] 包裹,且字符串建议加双引号(避免空格导致的语法错误)。

    字符表达式含义示例(判断 $str 是否为空)
    "$str1" == "$str2"字符串相等(==)"$name" == "root"
    "$str1" != "$str2"字符串不相等(!=)"$name" != "root"
    -z "$str"字符串为空(长度为 0)[ -z "$input" ]
    -n "$str"字符串非空(长度 > 0)[ -n "$input" ]

    注意== 前后必须有空格,且字符串变量必须加双引号(如输入包含空格时,"$str" 会视为一个整体,不加引号会被拆分为多个参数)。

    2.4 基于 “文件 / 目录” 

    用于 “判断文件是否存在、是文件还是目录”,是脚本中 “处理文件” 的常用条件,语法为 [ 文件表达式 文件路径 ]

    文件表达式含义示例(判断 /tmp/test.txt)
    -e文件 / 目录是否存在[ -e /tmp/test.txt ]
    -f是否为普通文件(非目录)[ -f /tmp/test.txt ]
    -d是否为目录[ -d /tmp/test_dir ]
    -r是否有读权限[ -r /tmp/test.txt ]
    -w是否有写权限[ -w /tmp/test.txt ]
    -x是否有执行权限[ -x /tmp/script.sh ]

     3. 多条件组合(and/or)

    当需要 “同时满足多个条件” 或 “满足任一条件” 时,可使用 逻辑运算符 组合条件:

    • and(并且):两个条件都为真,整体才为真,两种写法:
      • [ 条件1 -a 条件2 ](-a 是 and 的缩写,适用于单括号内);
      • [ 条件1 ] && [ 条件2 ](&& 是 Shell 通用的 “逻辑与”,更推荐,可读性高)。
    • or(或者):两个条件有一个为真,整体就为真,两种写法:
      • [ 条件1 -o 条件2 ](-o 是 or 的缩写);
      • [ 条件1 ] || [ 条件2 ](|| 是 Shell 通用的 “逻辑或”)。

    4、[ ] 和[[ ]]

    特性

    [ ](单括号)

    [[ ]](双括号)

    变量引用

    必须用双引号包裹变量(避免空值 / 特殊字符问题)

    可省略双引号(Bash 会自动处理空值)

    空格

    括号内外、运算符前后必须有空格

    相对宽松(但仍建议按规范加空格)

    命令执行结果

    需通过命令退出状态码间接判断1. 先执行命令(如 id "$user" &> /dev/null);
    2. 再用 $? 判断状态码(如 if [ $? -eq 0 ]; then ...)

    支持两种方式:

    1. 直接引用命令输出(如 [[ $(id -u "$user") -eq 0 ]],判断用户是否为 root);

    2. 直接判断命令状态(如 [[ id "$user" &> /dev/null ]])

    数值比较

    仅支持 POSIX 专用运算符:
    -eq、-gt、-ge、-lt、-le、-ne

    支持两种运算符:
    1. 兼容 POSIX 运算符(-eq/-gt 等);
    2. 支持直观符号(==、>、<、>=、<=、!=),与编程语言逻辑一致

    字符串比较

    1. 相等 / 不等:需用 ==/!=,且变量必须加双引号(避免空值、空格或通配符导致解析错误);
    2. 空值判断用 -z(空)、-n(非空),变量需加双引号

    1. 相等 / 不等支持 ==/!=,变量可省略双引号(Bash 自动处理空值、空格);
    2. 空值判断仍用 -z/-n,无需引号;
    3. 支持字典序比较(如 [[ "abc" > "abd" ]])

    字符/目录

    支持所有文件属性运算符-e 、-f 、-d 、-r 、-w 、-x 、-s 
    多条件需用 -a、-o 连接

    支持与单括号完全相同的文件属性运算符;
    多条件支持两种连接方式:
    1. 兼容 -a/-o;
    2. 推荐用 &&(逻辑与)、|| 、 !

    正则匹配

    不支持原生正则,需借助外部命令

    支持 =~直接正则匹配

    通配符匹配

    不支持原生通配符:通配符(*/?/[])会被 Shell 全局展开

    原生支持通配符匹配:通配符不会全局展开,直接作为模式匹配(如 [[ "$filename" == *.sh ]] 直接判断 “文件名是否以 .sh 结尾”;[[ "$str" == ??? ]] 判断 “字符串是否为 3 个字符”)

    逻辑运算符

    仅支持-a、-o、!

    支持&&、||、!

    Shell 中,单目表达式指 仅需要一个操作数(如文件路径、字符串变量) 

    所有文件属性类(-e/-f/-d 等)、字符串类(-z/-n)单目表达式,以及命令执行结果,均支持 ! 取反;! 必须放在条件表达式最前面,且前后需空格([ ! 表达式 ] 或 ! command);

    5、if嵌套

    练习:

    实战案例1:判断用户是否为 root 且当前目录是否为 /root

    #!/bin/bash
    username=$(whoami)  # 获取当前登录用户
    current_dir=$(pwd)  # 获取当前目录if [ "$username" == "root" ] && [ "$current_dir" == "/root" ]; thenecho "当前用户是 root,且当前目录是 /root(符合预期)"
    elseecho "当前用户:$username,当前目录:$current_dir(不符合 root + /root 的条件)"
    fi
    

      实战案例2:创建用户(存在则提示,不存在则创建)

    #!/bin/bash
    # 脚本路径:/opt/work/userCreate.sh
    read -p "请输入要创建的用户名:" name# 核心逻辑:用 id 命令的退出状态码判断用户是否存在
    id $name &> /dev/null  # 执行 id 命令,隐藏输出
    if [ $? -eq 0 ]; then  # $? 为 0 表示 id 命令成功(用户存在)echo "用户 ${name} 已存在,无需重复创建"
    else  # $? 非 0 表示用户不存在,执行创建useradd $name  # 创建用户echo "123456" | passwd --stdin $name &> /dev/null  # 批量设置密码(隐藏输出)echo "用户 ${name} 创建完成,初始密码:123456(建议首次登录修改)"
    fi
    

    实战案例3: 密码确认(两次输入一致则通过

    #!/bin/bash
    read -p "请输入密码:" pwd1
    read -p "请再次输入密码:" pwd2# 判断两次密码是否一致(变量加双引号,避免空格问题)
    if [ "$pwd2" == "$pwd1" ]; thenecho "密码设置成功:两次输入一致"
    elseecho "密码设置失败:两次输入不一致,请重新操作"
    fi
    

      实战案例4:判断文件是否有空行(显示空行行号)

    #!/bin/bash
    # 脚本路径:/opt/work/check_null_file.sh
    read -p "请输入要检测的文件路径(如 /tmp/info.txt):" file_name# 第一步:判断文件是否存在且为普通文件
    if [ -f "$file_name" ]; then# 第二步:判断文件是否有空行(^$ 匹配空行,&> /dev/null 隐藏输出)if grep "^$" "$file_name" &> /dev/null; thenecho "文件 ${file_name} 中存在空行,空行行号如下:"grep -n "^$" "$file_name"  # -n 显示行号elseecho "文件 ${file_name} 中无空行"fi
    elseecho "错误:文件 ${file_name} 不存在,或不是普通文件"
    fi
    

        实战案例5:根据当前小时判断时间段

    #!/bin/bash
    # 脚本路径:/opt/work/check_date.sh
    hour=$(date +%H)  # 获取当前小时(24小时制,如 09、13、18)# 多分支判断时间段
    if [ $hour -ge 8 -a $hour -le 12 ]; then  # -a 表示“并且”echo "当前时间段:上午(8:00-12:00)"
    elif [ $hour -gt 12 -a $hour -le 14 ]; thenecho "当前时间段:中午(12:00-14:00)"
    elif [ $hour -gt 14 -a $hour -le 18 ]; thenecho "当前时间段:下午(14:00-18:00)"
    elseecho "当前时间段:晚上(18:00-次日8:00)"
    fi
    

     实战案例6:判断用户是否存在(仅提示存在,不存在不处理)

    (1)id $用户名

    #!/bin/bash
    read -p "请输入要检查的用户名:" username
    # 用 id 命令判断用户是否存在(&> /dev/null 隐藏命令输出)
    if id $username &> /dev/null; thenecho "用户 $username 已存在"
    fi
    
    • id $username 命令的作用是查询用户 $username 的信息,用户存在状态码为 0。
    • &> /dev/null 的作用是隐藏命令的输出(无论成功或失败的信息都不显示在屏幕上),只保留退出状态码供 if 判断。
    • if 语句会根据命令的退出状态码判断:状态码为 0 则进入 then 分支,非 0 则跳过(或进入 else 分支)。

      (2)grep ^用户名: /etc/passwd

              

              /etc/passwd 文件的格式是每行对应一个用户,字段用冒号分隔。若直接用 grep "用户名" /etc/passwd 而不细化,可能匹配到非用户名字段包含该字符串的行。

      实战案例7:比较uid和gid

      #!/bin/bash# 检查是否提供了用户名参数
      if [ $# -ne 1 ]; thenecho "请输入用户名作为参数: $0 <用户名>"exit 1
      fiusername="$1"# 检查用户是否存在
      if ! id -u "$username" >/dev/null 2>&1; thenecho "错误: 用户 '$username' 不存在"exit 1
      fi# 获取UID和GID
      uid=$(id -u "$username")
      gid=$(id -g "$username")# 比较UID和GID
      if [ "$uid" -eq "$gid" ]; thenecho "gooduser"
      elseecho "baduser"
      fi
      

       实战案例8:

      1. 备份所有.repo 文件到 backup 目录
      2. 挂载光盘到 /mnt/cdrom
      3. 创建本地源配置文件

        实战案例9:提示用户输入数据,判断是否输入LInux(linux)显示红帽,输入windows(Windows)显示微软,输入macos(Macos)显示苹果,其他

      实战案例10:检测磁盘。用户输入磁盘的挂载点,判断硬盘占用容量70%>显示警告,否则正常

       实战案例11:检测磁盘使用率(超过 70% 警告)

      #!/bin/bash
      # 脚本路径:/opt/work/check_disk.sh
      read -p "请输入要检测的磁盘(如 /dev/sda1):" disk_name# 第一步:先判断磁盘是否存在(用 df -hT 过滤磁盘名)
      df -hT | grep -w "${disk_name}$" &> /dev/null  # -w 匹配完整单词,避免误判
      if [ $? -ne 0 ]; thenecho "错误:磁盘 ${disk_name} 不存在,请检查输入(可用 df -hT 查看所有磁盘)"exit 1  # 退出脚本,状态码 1 表示错误
      fi# 第二步:提取磁盘使用率(去掉 % 符号,转为纯数字)
      # df 输出示例:/dev/sda1  ext4  50G  30G  20G  60% /
      usage=$(df -hT | grep -w "${disk_name}$" | awk '{print $6}' | awk -F% '{print $1}')# 第三步:判断使用率是否超过 70%
      if [ $usage -gt 70 ]; thenecho "警告:磁盘 ${disk_name} 使用率已达 ${usage}%,超过阈值 70%,请及时清理!"
      elseecho "正常:磁盘 ${disk_name} 使用率为 ${usage}%,低于阈值 70%"
      fi
      

      实战案例12:检测mysqlio线程的状态,若正常则输出1,否则显示0

      实战案例13:输入服务名称,判断是否运行

      服务名称;pid 3,端口,输出服务启动时间

      (三)case 条件判断:高效处理等值匹配

      当需要 “判断变量是否等于多个固定值” 时(如根据参数执行不同操作、根据输入选择功能),case 判断比 if 更简洁(避免多个 elif 嵌套),核心是# “等值匹配”。

      1. case 判断的基础语法

      case $变量 in  # 变量可以是参数、用户输入等值1)  # 若变量等于“值1”执行操作1;;  # 结束当前分支(必须写,两个分号)值2|值3)  # 若变量等于“值2”或“值3”(用 | 分隔多个值)执行操作2;;*)  # 默认分支(所有值都不匹配时执行,类似 if 的 else)执行操作3;;
      esac  # 结束 case 判断(case 的反向拼写)
      

      2. 实战案例:基于参数的多功能脚本

      案例 1:根据输入的操作系统,输出对应厂商
      #!/bin/bash
      read -p "请输入操作系统名称(如 Linux/Windows/Mac):" oscase $os inLinux|linux|LINUX)  # 支持大小写(用 | 分隔多个等值)echo "操作系统厂商:红帽、Ubuntu 等(基于 Linux 内核)";;Windows|windows|WINDOWS)echo "操作系统厂商:微软(Microsoft)";;Mac|mac|macOS)echo "操作系统厂商:苹果(Apple)";;*)  # 不匹配任何值时的默认处理echo "暂不支持该操作系统:$os(仅支持 Linux/Windows/Mac)";;
      esac
      
      案例 2:用位置变量实现 “脚本参数控制”

      Shell 中的 位置变量 用于获取脚本执行时的参数(如 ./script.sh arg1 arg2 中,$1=arg1$2=arg2),结合 case 可实现 “多参数功能”:

      • $0:脚本自身名称(如 ./check_process.sh 中,$0=check_process.sh);
      • $1~${n}:第 1 到第 n 个参数(${10} 需加大括号,避免与 $1 混淆);
      • $#:参数的总个数(判断是否传入参数)。

      实战案例:检测进程状态(支持参数指定进程名)

      #!/bin/bash
      # 脚本路径:/opt/work/check_process.sh
      # 用法:./check_process.sh 进程名(如 ./check_process.sh nginx)# 第一步:判断是否传入参数($# 为 0 表示无参数)
      if [ $# -eq 0 ]; thenecho "用法错误:请指定要检测的进程名"echo "示例:$0 nginx(检测 nginx 进程)、$0 mysql(检测 mysql 进程)"exit 1
      fiprocess=$1  # 将第一个参数赋值给 process 变量# 第二步:用 case 判断进程状态(先通过 ps 检查进程是否存在)
      ps -elf | grep -w "$process" | grep -v "grep" &> /dev/null  # 排除 grep 自身
      if [ $? -ne 0 ]; thenecho "状态:进程 ${process} 未启动"
      else# 提取进程详情:PID、启动时间、端口(需安装 netstat)proc_id=$(pidof $process)  # 获取进程 PID(多个 PID 用空格分隔)start_time=$(ps -p $proc_id -o lstart=)  # 获取启动时间(如 Wed Jul 10 09:30:00 2024)# 提取端口(仅监听端口,

      待细化

      http://www.xdnf.cn/news/19079.html

      相关文章:

    • linux中.tar 解压命令
    • 【系列05】端侧AI:构建与部署高效的本地化AI模型 第4章:模型量化(Quantization)
    • 嵌入式Linux驱动开发 - DTS LED驱动
    • 管家婆辉煌ERP中如何查询畅销商品
    • java8浮点型算平均值
    • 37 HTB Remote 机器 - 容易
    • 字典解密助手ArchiveHelperWpfv1.0.12详细使用说明书
    • Apisix工作流程
    • 界面钝化新策略:华南理工实现泡沫铜/Bi-In相变材料热循环性能显著增强
    • 直流电机驱动与TB6612
    • Excel数组学习笔记
    • 【开题答辩全过程】以 基于JSP的养生网站系统为例,包含答辩的问题和答案
    • 本地部署商业服务器 Glassfish 并实现外部访问
    • Rust 安装与运行指南
    • Jetson进行旋转目标检测推理实现大疆无人机飞行控制
    • Git 9 ,.git/index.lock 文件冲突问题( .git/index.lock‘: File exists. )
    • 卷积神经网络为什么要填充(Padding)
    • 基于无人机的风电叶片全自动智能巡检:高精度停角估计与细节优先曝光调控技术
    • 在做题中学习(89):合并区间
    • 如何去除edge浏览器的灰色边框
    • idea2023.3遇到了Lombok失效问题,注释optional和annotationProcessorPaths即可恢复正常
    • Redis与MySQL数据不一致问题
    • 【MYSQL | 基础篇 多表查询】
    • FunctionAI 图像生成:简化从灵感到 API 调用的每一步
    • Kingbase-Mysql兼容模式下LOAD DATA INFILE语法再体验
    • idea控制台从properties中取值然后打印出现控制台乱码问题
    • (论文速读)MAPTNet——少样本表面缺陷分割方法
    • Java图形图像处理【基础篇】【二】
    • 前端-什么是Vue
    • DMZ层Nginx TLS 终止与安全接入配置实战20250829