示例说明
卡死的命令比失败的命令更危险——它占着资源与管道直到有人来杀。timeout 在秒级上限后发 SIGTERM,–kill-after 再宽限五秒还不退就 SIGKILL,两级递进对付赖死进程。退出码是分类依据:124 专属超时值得重试,137 说明进程连 TERM 都不理,真实失败码直接上抛——三类处置各异。
技术要点
- 124 退出码专属超时语义
- kill-after 两级递进杀顽固进程
- 按退出码分类决定重试或上抛
代码解读
run_with_timeout 包装命令循环执行:成功即回零;超时递增尝试计数(算术命令的 || true 防 -e 误杀);强杀与普通失败立即返回原码。MAX_RETRIES 加一次是总尝试次数的常见表达。两个调用示例分别包裹迁移脚本与 HTTP 探测,超时重试对幂等命令才有意义。
使用建议
</dev/null 防命令等待交互输入。重试间隔可按尝试数递增。数据库迁移这类非幂等命令的超时重试要先确认中断点安全。
代码示例
#!/usr/bin/env bash
set -euo pipefail
readonly TIMEOUT_SECS=30
readonly MAX_RETRIES=2
run_with_timeout() {
local label="$1"
shift
local attempt=1
while (( attempt <= MAX_RETRIES + 1 )); do
timeout --kill-after=5s "${TIMEOUT_SECS}s" "$@" </dev/null
local code=$?
case "${code}" in
0)
echo "${label}: 成功"
return 0
;;
124)
echo "Error: ${label} 第 ${attempt} 次超时(${TIMEOUT_SECS}s)" >&2
(( attempt++ )) || true
;;
137)
echo "Error: ${label} 被强杀(TERM 后 5s 未退出)" >&2
return 137
;;
*)
echo "Error: ${label} 失败,退出码 ${code}" >&2
return "${code}"
;;
esac
done
echo "Error: ${label} 超时重试耗尽" >&2
return 124
}
# 用法:超时会重试,其他失败立即上抛
run_with_timeout "数据库迁移" ./migrate.sh --dry-run
run_with_timeout "健康探测" curl -sf http://127.0.0.1:8080/healthz