1️⃣ 测试表达式
Shell 中的条件判断通过 测试表达式 来实现,有两种常用形式:
| 形式 | 说明 | 推荐度 |
|---|---|---|
[ 表达式 ] | 传统测试命令,POSIX 兼容 | ✓ |
[[ 表达式 ]] | Bash 扩展,支持 &&/|| 和正则,更安全 | ✓✓✓ |
(( 表达式 )) | 数值算术测试 | ✓✓ |
文件测试
# -e 存在 -f 普通文件 -d 目录 -s 非空文件 # -r 可读 -w 可写 -x 可执行 # -L 是符号链接 -b 块设备 -c 字符设备 [[ -f "/etc/passwd" ]] && echo "文件存在" [[ -d "/tmp" ]] && echo "是目录" [[ -x "/usr/bin/python3" ]] && echo "可执行" [[ -s "/var/log/syslog" ]] && echo "非空文件" bash
字符串测试
name="张运维" [[ -z "$name" ]] && echo "空字符串" # -z: 为空 [[ -n "$name" ]] && echo "非空字符串" # -n: 非空 [[ "$name" == "张运维" ]] && echo "相等" [[ "$name" != "admin" ]] && echo "不相等" # [[ ]] 支持通配符匹配 [[ "$name" == 张* ]] && echo "姓张的" # [[ ]] 支持正则表达式匹配 =~ email="user@example.com" [[ "$email" =~ ^[a-zA-Z0-9.]+@[a-zA-Z0-9.]+\.[a-z]{2,}$ ]] && echo "邮箱格式正确" bash
数值比较
a=10 b=20 # 数值比较操作符 [[ $a -eq $b ]] && echo "相等" # equal [[ $a -ne $b ]] && echo "不相等" # not equal [[ $a -lt $b ]] && echo "a小于b" # less than [[ $a -le $b ]] && echo "a不大于b" # less or equal [[ $a -gt $b ]] && echo "a大于b" # greater than [[ $a -ge $b ]] && echo "a不小于b" # greater or equal # 使用 (( )) 更直观 (( a < b )) && echo "a小于b" (( a + b == 30 )) && echo "和为30" bash
2️⃣ if 条件判断
#!/bin/bash # 语法:if/elif/else/fi score=85 if (( score >= 90 )); then echo "优秀" elif (( score >= 80 )); then echo "良好" elif (( score >= 60 )); then echo "及格" else echo "不及格" fi bash
实用:判断参数与文件存在
#!/bin/bash # 检查脚本是否提供了参数 if [[ $# -lt 1 ]]; then echo "用法:$0 <文件名>" exit 1 fi file="$1" if [[ -f "$file" ]]; then echo "✓ 文件存在,共 $(wc -l <"$file") 行" elif [[ -d "$file" ]]; then echo "这是一个目录,包含 $(ls "$file" | wc -l) 个文件" else echo "✗ 文件或目录不存在:$file" exit 1 fi bash
3️⃣ case 模式匹配
当有多个固定值需要匹配时,case 比多个 elif 更清晰:
#!/bin/bash read -p "请输入操作 [start|stop|restart|status]: " action case "$action" in start) echo "正在启动服务..." ;; stop) echo "正在停止服务..." ;; restart|reload) # 多个匹配值用 | 分隔 echo "正在重启服务..." ;; status) echo "查看服务状态..." ;; [0-9]*) # 支持通配符 echo "输入了数字" ;; *) # 默认(相当于 else) echo "无效操作:$action" exit 1 ;; esac bash
4️⃣ for 循环
for in 列表循环
# 遍历列表 for item in apple banana orange; do echo "水果: $item" done # 遍历数组 servers=("web01" "web02" "db01") for server in "${servers[@]}"; do echo "检查服务器:$server" done # 遍历文件 for file in /etc/*.conf; do echo "配置文件:$file" done # 遍历命令输出 for user in $(awk -F: '$3 >= 1000 {print $1}' /etc/passwd); do echo "普通用户:$user" done bash
C 风格 for 循环
# C 风格:for (( 初始; 条件; 步进 )) for (( i=1; i<=10; i++ )); do echo "第 $i 次" done # seq 生成序列 for i in $(seq 1 5); do echo "$i" done # Bash 花括号展开生成序列 for i in {1..5}; do echo "$i" done # 带步长 for i in {0..100..10}; do # 0 10 20 ... 100 echo "$i" done bash
5️⃣ while 与 until 循环
# while:条件为真时执行 count=1 while (( count <= 5 )); do echo "count = $count" (( count++ )) done # until:条件为假时执行(与 while 相反) count=1 until (( count > 5 )); do echo "count = $count" (( count++ )) done # 无限循环 while true; do echo "每5秒检查一次..." sleep 5 done # 逐行读取文件(推荐写法) while IFS= read -r line; do echo "处理:$line" done < "/etc/hosts" bash
6️⃣ break、continue 与 exit
# break:跳出当前循环 for i in {1..10}; do [[ $i -eq 5 ]] && break # i==5时退出循环 echo "$i" done # continue:跳过本次迭代 for i in {1..10}; do (( i % 2 == 0 )) && continue # 跳过偶数 echo "奇数:$i" done # break N:跳出 N 层嵌套循环 for i in {1..3}; do for j in {1..3}; do [[ $i -eq 2 && $j -eq 2 ]] && break 2 # 跳出两层 echo "i=$i j=$j" done done # exit:退出脚本 # exit 0 = 成功 # exit 1 = 通用错误 # exit N = 自定义退出码(0-255) bash
7️⃣ 综合示例:服务检查脚本
#!/bin/bash # 批量检查服务状态 services=("sshd" "nginx" "mysql" "firewalld") ok_count=0 fail_count=0 echo "===== 服务状态检查 $(date '+%Y-%m-%d %H:%M:%S') =====" for svc in "${services[@]}"; do if systemctl is-active "$svc" >/dev/null 2>&1; then printf " ✓ %-15s [运行中]\n" "$svc" (( ok_count++ )) else printf " ✗ %-15s [已停止]\n" "$svc" (( fail_count++ )) fi done echo "-----" echo "运行:$ok_count 停止:$fail_count" [[ $fail_count -gt 0 ]] && exit 1 || exit 0 bash
🏋️ 今日练习
- 写一个猜数字游戏:随机生成1-100的数字,让用户猜测,提示偏大/偏小,记录猜测次数
- 写一个脚本遍历 /etc 下所有 .conf 文件,统计各文件行数,输出最大和最小的5个
- 用 case 语句实现一个简单菜单:1-查看磁盘 2-查看内存 3-查看网络 4-退出
- 写一个脚本:批量 ping 192.168.1.1~192.168.1.254,收集可达的IP地址到文件中
- 用 while 循环监控某个文件是否存在,每秒检查一次,文件出现后输出通知