在 Shell 循环中应通过显式检查 $? 并用 if 分支处理失败(如记录日志、跳过、continue),避免 set -e 或 || exit 导致中断;推荐不用 set -e,改用逐命令判断,必要时捕获 stderr 区分错误类型。

在 Linux Shell 脚本中,让循环继续执行的关键是把错误处理控制在单次迭代内部,而不是让整个循环因一次失败而中断。这和 Java 中的 try-catch 放在循环体内是一个逻辑:每次操作独立兜底。
用 if 判断命令退出码,失败时不 exit
Shell 没有 try-catch,但可以用 $? 捕获上一条命令的退出状态,并用 if 分支做差异化处理:
- 命令成功(
$? == 0)→ 正常走后续逻辑 - 命令失败(
$? != 0)→ 记录日志、设默认值或跳过,然后continue
示例(遍历文件并压缩,某个文件权限不足也不中断):
for file in *.log; do
if ! gzip "$file" 2>/dev/null; then
echo "WARN: failed to compress $file, skipping..." >&2
continue
fi
echo "Compressed: $file.gz"
done
结合 set -e 时要格外小心
如果脚本开头用了 set -e(遇到非零退出立即终止),它会让整个脚本在单次命令失败时退出——这会直接打断循环。
解决办法有两种:
- 临时关闭:
set +e放在循环前,循环结束后再set -e恢复 - 更推荐:不用
set -e,改用显式判断,控制力更强、意图更清晰
重定向 stderr 并检查关键错误类型
有些失败需要区分对待:比如网络超时可重试,而“文件不存在”应直接跳过。可把 stderr 临时捕获后分析:
for url in "${urls[@]}"; do
log_tmp=$(mktemp)
if ! curl -s -f "$url" 2>"$log_tmp"; then
if grep -q "Connection refused\|timeout" "$log_tmp"; then
echo "Retrying $url..."
sleep 1 && curl -s -f "$url" || echo "Still failed: $url"
else
echo "Skip invalid URL: $url"
fi
fi
rm -f "$log_tmp"
done
避免隐式中断的常见坑
以下写法看似无害,实则容易意外终止循环:
-
gzip "$file" || exit 1→ 一错就退出整个脚本 -
set -e+ 循环内未加判断的命令 → 第一次失败即中断 - 用管道但没处理中间命令失败,如
cat $file | grep error | wc -l,若cat失败,整条管道返回非零,if判断可能误判
稳妥做法是:对每条可能失败的命令单独判断,不依赖全局开关,也不假设管道行为。


















