🐚 C 风格判断/循环、并发检测、交互式 select 清单

目标读者:已经掌握基本 if/for/while 的同学
环境建议:#!/usr/bin/env bash + set -euo pipefail(更稳)


1) C 语言风格的条件判断:(( ))[[ ]]

在 Bash 里有三套常见判断语法:

  • test / [ ]:传统 POSIX,功能基础。
  • [[ ]]:Bash 扩展,字符串/模式匹配强,不受路径名扩展影响。
  • (( ))算术上下文,C 语法手感,0/非 0 逻辑与 C 类似。

1.1 数值判断:(( ))(推荐做整数比较)

#!/usr/bin/env bash
set -euo pipefail

a=10 b=20
if (( a < b )); then           # 像 C 一样用 < > == != += -=
  echo "a < b"
fi

(( a += 3, b -= 5 ))            # 逗号连接多条算术语句
echo "a=$a, b=$b"               # a=13, b=15

# 作为条件使用:表达式非 0 为真
(( a - b )) || echo "a == b"

优点:无需 -lt/-gt 等;变量可不加 $;错误更少。
坑点:仅适用于整数;浮点请用 bcawk

1.2 字符串与模式判断:[[ ]]

file="report_2025.txt"
if [[ $file == report_*\.txt ]]; then  # 支持通配符,不会被路径扩展干扰
  echo "匹配到报告文件"
fi

s="Hello World"
if [[ $s =~ ^Hello[[:space:]]+World$ ]]; then   # 正则匹配(=~)
  echo "正则匹配成功"
fi

优点== 模式匹配、=~ 正则、&&/|| 原生短路。
坑点=~ 正则右侧不要加引号,否则变成字面量字符串。

1.3 何时用哪种?

  • 整数比较(( ))
  • 字符串/正则/模式[[ ]]
  • 需要 POSIX 兼容[ ](但功能弱)

2) C 风格 for 循环:for (( init; cond; step ))

Bash 支持 C 语法 for,写数值循环更直观。

#!/usr/bin/env bash
set -euo pipefail

for (( i=0; i<5; i++ )); do
  printf "i=%d\n" "$i"
done

# 倒序、步长
for (( i=10; i>=0; i-=2 )); do
  echo "$i"
done

2.1 嵌套循环 & 组合判断

for (( i=1; i<=3; i++ )); do
  for (( j=1; j<=3; j++ )); do
    (( (i+j)%2==0 )) && echo "i=$i j=$j -> 偶数和"
  done
done

2.2 与数组搭配(索引循环)

arr=(alpha beta gamma)
for (( i=0; i<${#arr[@]}; i++ )); do
  echo "$i: ${arr[i]}"
done

对比:遍历值的方式是 for x in "${arr[@]}"; do ...; done
索引循环便于配对、并行地访问多个数组。


3) 并发检测:让脚本“多线程思维”

运维/数据脚本里最常见诉求:并发地做一堆检测或请求,加速 N 倍。
Bash 并没有线程,但我们有三把利器:后台任务 (&)wait 汇总并发控制

3.1 最小并发原型:后台 + wait

check_host(){
  host="$1"
  if ping -c1 -W1 "$host" >/dev/null 2>&1; then
    echo "✅ $host 通"
  else
    echo "❌ $host 不通"
  fi
}

hosts=(1.1.1.1 8.8.8.8 192.0.2.99)
for h in "${hosts[@]}"; do
  check_host "$h" &
done
wait                # 等所有后台任务结束
echo "全部检测完成"

3.2 控制并发度(轻量 semaphore)

命名管道作业计数限制并发,例如最多 5 并发:

max_jobs=5
jobcnt=0
for h in "${hosts[@]}"; do
  check_host "$h" &
  (( ++jobcnt >= max_jobs )) && { wait -n; ((jobcnt--)); }  # Bash 5: wait -n 等任一完成
done
wait

若是 Bash 4,可用数组记录 PID,或用 xargs -PGNU parallel 替代。

3.3 xargs 并发(无需写循环)

printf "%s\n" "${hosts[@]}" | xargs -n1 -P4 -I{} bash -c '
  h="$1"
  if ping -c1 -W1 "$h" >/dev/null 2>&1; then
    echo "✅ $h"
  else
    echo "❌ $h"
  fi
' _ {}
  • -P4:4 并发
  • -n1:每次传 1 个参数
  • -I{}:占位符

3.4 GNU parallel(更强更优雅)

# apt/yum 安装 parallel 后使用
parallel -j 8 'ping -c1 -W1 {} >/dev/null && echo ✅ {} || echo ❌ {}' ::: "${hosts[@]}"

3.5 实战:并发检测 HTTP 健康

check_http(){
  url="$1"
  code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 2 "$url" || echo 000)
  if (( code>=200 && code<400 )); then
    printf "✅ %-30s %s\n" "$url" "$code"
  else
    printf "❌ %-30s %s\n" "$url" "$code"
  fi
}

mapfile -t urls < urls.txt    # 每行一个 URL
max=10; cnt=0
for u in "${urls[@]}"; do
  check_http "$u" &
  (( ++cnt >= max )) && { wait -n; ((cnt--)); }
done
wait

并发三要点

  • 输出乱序?考虑为每个任务缓冲到临时文件,最后统一汇总。
  • 接口限流?增加 sleep 随机抖动;或用 token bucket 控制节流。
  • 错误处理?统计失败计数,全部结束后统一给出汇总与非零退出码。

4) select 清单交互:写个“半图形化”菜单

Bash 自带 select 语法,能快速做命令行菜单。适合跳板、工具集入口、批处理选择场景。

4.1 入门示例

#!/usr/bin/env bash
set -euo pipefail

PS3="请选择要管理的服务(1-3,q退出):"
options=("nginx" "redis" "postgres" "退出")

select opt in "${options[@]}"; do
  case "$REPLY" in
    1) echo "你选择了 nginx"; break ;;
    2) echo "你选择了 redis"; break ;;
    3) echo "你选择了 postgres"; break ;;
    q|Q|4) echo "再见"; exit 0 ;;
    *) echo "非法选择:$REPLY" ;;
  esac
done
  • PS3:提示语
  • select 自动打印编号列表;用户输入数字即可
  • 变量:$opt 为选中项文本,$REPLY 为原始输入

4.2 绑定动作的菜单

manage_nginx(){ sudo systemctl "$1" nginx; }
manage_redis(){ sudo systemctl "$1" redis; }

PS3="选择服务:"
services=("nginx" "redis" "退出")
select s in "${services[@]}"; do
  case "$s" in
    nginx)  act=("start" "stop" "restart" "status");;
    redis)  act=("start" "stop" "restart" "status");;
    退出)   exit 0 ;;
    *)      echo "无效"; continue ;;
  esac

  PS3="选择动作:"
  select a in "${act[@]}"; do
    case "$a" in
      start|stop|restart|status)
        "manage_${s}" "$a"; break ;;
      *) echo "无效动作" ;;
    esac
  done
done

进阶建议

  • 配合 dialog/whiptail 可做出更好看的 TUI;
  • 配合配置文件生成菜单(动态扩展服务与动作)。

5) 实战组合:C 风格 + 并发 + select 的“巡检工具”

这个小工具把本文的三块内容糅合起来:

  • select 选择“主机批量巡检 / URL 健康检查 / 退出”
  • 并发加速检测
  • (( )) 实现数值逻辑与统计
#!/usr/bin/env bash
set -euo pipefail

PS3="请选择功能:"
menu=("主机存活巡检(ping)" "URL 健康检查(HTTP)" "退出")
select m in "${menu[@]}"; do
  case "$REPLY" in
    1)  # ping 巡检
        mapfile -t hosts < hosts.txt
        max=8; cnt=0; ok=0; bad=0
        check(){
          h="$1"
          if ping -c1 -W1 "$h" >/dev/null 2>&1; then
            echo "✅ $h"
            (( ok++ ))
          else
            echo "❌ $h"
            (( bad++ ))
          fi
        }
        for h in "${hosts[@]}"; do
          check "$h" &
          (( ++cnt >= max )) && { wait -n; ((cnt--)); }
        done
        wait
        echo "统计:OK=$ok, BAD=$bad"
        ;;
    2)  # URL 健康
        mapfile -t urls < urls.txt
        max=10; cnt=0; ok=0; bad=0
        check(){
          u="$1"
          code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 2 "$u" || echo 000)
          if (( code>=200 && code<400 )); then
            printf "✅ %-40s %s\n" "$u" "$code"
            (( ok++ ))
          else
            printf "❌ %-40s %s\n" "$u" "$code"
            (( bad++ ))
          fi
        }
        for u in "${urls[@]}"; do
          check "$u" &
          (( ++cnt >= max )) && { wait -n; ((cnt--)); }
        done
        wait
        echo "统计:OK=$ok, BAD=$bad"
        ;;
    3|q|Q) echo "再见"; exit 0 ;;
    *) echo "无效选择";;
  esac
done

6) 踩坑清单(务必收藏)

  • [[ ... ]] 中做正则匹配 =~ 时,右侧不要加引号

  • (( )) 只适合整数;浮点用 bcawk

  • 并发时输出可能乱序,需要缓冲到文件或使用有序输出策略。

  • xargs -Pparallel 默认不保序,如需保序看其 --keep-order/-k 选项。

  • 大规模并发前先压测目标(端口、接口)承受能力,避免被当作攻击。

  • select$REPLY 是用户输入,不一定合法;别忘了 case 里兜底分支。

  • 全文建议加:

    #!/usr/bin/env bash
    set -euo pipefail
    IFS=$'\n\t'
    

7) 一页速记(Cheat Sheet)

  • 整数判断(( a>b && b>0 ))

  • 字符串/正则[[ $s =~ ^foo|bar$ ]]

  • C forfor((i=0;i<n;i++)); do ...; done

  • 后台并发cmd & ... ; wait

  • 限速并发(Bash 5):

    ((++cnt>=N)) && { wait -n; ((cnt--)); }
    
  • xargs 并发printf '%s\n' "${arr[@]}" | xargs -n1 -P8 cmd

  • select

    PS3="选择:"; select x in A B 退出; do ...; done
    

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐