
Linux Shell 脚本中实现超时控制,最直接可靠的方式是使用系统自带的 timeout 命令。它专为限时执行命令而设计,支持信号控制、退出码区分、输出捕获等,无需手动 fork + sleep + kill,避免竞态和逻辑漏洞。
timeout 命令基础用法
语法简洁,核心参数明确:
- timeout 5s ping -c 3 baidu.com:命令运行超过 5 秒则被终止
- timeout -k 2s 10s ./long_run.sh:硬超时 10 秒,若未响应则 2 秒后发 SIGKILL 强杀
- timeout --signal=SIGTERM 8s curl http://api.example:指定发送 SIGTERM(默认是 SIGTERM,可显式写出)
捕获超时状态与错误处理
timeout 退出码有明确语义,可用于判断是否超时:
- 命令正常结束 → 退出码为原命令退出码(如 0 表示成功)
- 因超时被终止 → 退出码为 124(标准定义)
- 因信号被杀(非 timeout 触发)→ 退出码为 128 + signal_num(如 SIGKILL=9 → 137)
- 示例判断:if timeout 3s ./task.sh; then echo "ok"; else case $? in 124) echo "timeout";; *) echo "failed";; esac; fi
替代方案:纯 Bash 实现(仅限简单场景)
不依赖 timeout 命令时(如极简容器或旧系统),可用子 shell + trap + background job 模拟,但需注意局限性:
- 无法可靠中断内建命令(如 read、wait)或已阻塞的系统调用
- 需避免子 shell 变量泄漏,建议封装为函数
- 简易示例:(sleep 5; kill $! 2>/dev/null) & pid=$!; ./slow_cmd & wait $! 2>/dev/null; kill $pid 2>/dev/null
- 更健壮的做法是用 bash -c 'ulimit -t 5; exec "$@"' _ command(基于 CPU 时间,非挂钟时间,适用场景有限)
常见陷阱与建议
实际使用中容易忽略的关键点:
- timeout 默认只杀目标进程,不递归终止其子进程;加 --preserve-status 可保留原命令退出码逻辑(v8.23+)
- 在管道中使用 timeout 需包裹整个 pipeline,例如:timeout 3s sh -c 'cmd1 | cmd2',而非只包 cmd1
- 脚本中调用外部程序前,先检查 timeout 是否存在:command -v timeout >/dev/null || { echo "timeout not available"; exit 1; }
- 测试超时行为时,用 sleep 或 yes >/dev/null 模拟长任务,避免依赖网络或 I/O 不稳定因素









