← 中文资源

简体中文版本

Timeout 包装命令执行控制

用 timeout 工具给外部命令加硬性时限:124 是超时专属退出码可安全重试,137 是强杀信号,其余失败立即上抛。

阅读英文原文

示例说明

卡死的命令比失败的命令更危险——它占着资源与管道直到有人来杀。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