Skip to content

流程控制

  • 状态:已验证
  • 来源:对话整理
  • 更新时间:2026-03-12

关键结论

  • Shell 流程控制主要围绕条件判断、分支、循环、函数、退出码和逻辑运算展开。
  • ifcaseforwhile read -r 和函数是最常见的控制结构。

详细分析

  • Shell 中多数逻辑错误都来自判断条件、引号、循环读入方式或函数返回方式不清晰。
  • Bash 的 [[ ... ]](( ... )) 在表达复杂条件时更直观,但需要明确 Bash 依赖。

可执行步骤

  1. 先确定当前脚本是否允许使用 Bash 扩展语法。
  2. 依据具体场景选择 ifcase、循环或函数。
  3. 涉及读文件时优先使用 IFS= read -r
  4. 对函数结果区分“退出码返回”和“标准输出返回”。

命令 / 配置 / 代码

高频写法清单:

  • if [ ... ]; then ... fi
  • if [[ ... ]]; then ... fi
  • case ... in ... esac
  • for x in ...; do ... done
  • while read -r line; do ... done
  • name() { ... }
  • cmd1 && cmd2

条件判断

  • if ...; then ... fi 条件分支。
  • [ ... ] 传统测试表达式。
  • [[ ... ]] Bash 扩展测试表达式,支持更强匹配能力。
  • [ "$a" = "$b" ] 字符串相等。
  • [ "$a" != "$b" ] 字符串不等。
  • [ "$n" -lt 10 ] 数值比较。
  • [ -f "$file" ] 判断普通文件是否存在。
  • [ -d "$dir" ] 判断目录是否存在。
  • [ -n "$x" ] 字符串非空。
  • [ -z "$x" ] 字符串为空。
  • [[ "$name" == a* ]] Bash 模式匹配。
  • [[ "$text" =~ ^[0-9]+$ ]] Bash 正则匹配。
  • (( n > 0 )) 算术条件判断。
bash
if [ -f "$file" ]; then
  echo "file exists"
fi
bash
if [ "$a" = "$b" ]; then
  echo "equal"
elif [ "$a" != "$b" ]; then
  echo "not equal"
else
  echo "unknown"
fi
bash
if [[ -f "$file" && "$name" == a* ]]; then
  echo "match"
fi

case 分支

  • case value in ... esac 多分支匹配。
  • pattern) 匹配某个模式。
  • *) 默认分支。
  • ;; 结束当前分支。
bash
case "${1:-}" in
  start)
    echo "start"
    ;;
  stop)
    echo "stop"
    ;;
  *)
    echo "unknown"
    ;;
esac

for 循环

  • for item in list; do ... done 遍历列表。
  • for ((i = 0; i < n; i++)); do ... done C 风格计数循环。
bash
for f in *.md; do
  echo "$f"
done
bash
for ((i = 0; i < 5; i++)); do
  echo "$i"
done

select 选择循环

  • select item in list; do ... done 生成简单菜单,适合交互脚本。
  • break 退出选择循环。
bash
select action in start stop quit; do
  case "$action" in
    start|stop) echo "$action" ;;
    quit) break ;;
    *) echo "invalid" ;;
  esac
done

whileread

  • while condition; do ... done 条件循环。
  • read -r line 读取一行文本,不处理反斜杠转义。
  • IFS= read -r line 保留行首尾空白并安全读取整行。
  • done < input.txt 从文件重定向输入。
bash
while read -r line; do
  echo "$line"
done < input.txt
bash
while IFS= read -r line; do
  echo "$line"
done < input.txt

函数

  • name() { ... } 定义函数。
  • local var="x" 定义函数局部变量。
  • return 0 返回退出码。
  • echo ... 函数若需“返回字符串”,通常通过标准输出返回。
bash
log() {
  echo "[INFO] $*"
}
bash
add() {
  local a="$1"
  local b="$2"
  echo $((a + b))
}

source 与脚本执行

  • ./script.sh 启动子进程执行脚本。
  • bash script.sh 用 Bash 执行脚本。
  • source script.sh 在当前 shell 中加载脚本。
  • . script.shsource 的简写。
bash
source ./env.sh
. ./env.sh
bash ./build.sh

退出码

  • exit 0 整个脚本成功退出。
  • exit 1 整个脚本失败退出。
  • return 0 函数成功返回。
  • return 1 函数失败返回。
  • "$?" 上一条命令的退出码。
bash
echo "$?"

逻辑运算

  • cmd1 && cmd2 前一条成功才执行后一条。
  • cmd1 || cmd2 前一条失败才执行后一条。
  • ! cmd1 逻辑取反。
bash
cmd1 && cmd2
cmd1 || cmd2
! cmd1

子 shell 与命令组

  • ( cmd1; cmd2 ) 在子 shell 中执行命令组。
  • { cmd1; cmd2; } 在当前 shell 中执行命令组。
bash
(cd /tmp && pwd)
{ echo "a"; echo "b"; }

风险与注意事项

  • [ ... ] 两侧必须有空格,例如 [ -f "$x" ]
  • [[ ... ]] 是 Bash 写法,不是所有 /bin/sh 都支持。
  • 逐行读取文本优先使用 read -r
  • set -e 在某些复合语句中的行为并不直观,关键脚本应结合退出码显式处理。