timeout命令通过信号机制终止进程,默认发SIGTERM(15),若进程忽略则需改用SIGKILL或-k选项;单位必须显式标注(如0.5s而非0.5);退出码124表示超时,管道需用sh -c包裹才能整体管控。

timeout 命令不是“包装器”或“装饰器”,它本身不修改子程序逻辑,而是靠信号机制在超时后终止进程树——所以它能用,但必须理解信号怎么传、谁会被杀。
timeout 后命令没退出?多半是信号被忽略或没传下去
默认 timeout 发送 SIGTERM(15号信号),但很多程序会捕获并忽略它,比如某些 shell 脚本里写了 trap '' TERM,或者 ffmpeg 在写入关键帧时会阻塞信号。这时候命令看似“卡住”,其实是没响应 SIGTERM。
- 先确认目标命令是否真能响应
SIGTERM:手动运行它,另开终端执行kill -TERM $PID,看是否退出 - 若不退出,改用
-s SIGKILL或配合-k选项:例如timeout -s SIGTERM -k 2s 5s your_cmd表示 5 秒发SIGTERM,再等 2 秒没退就发SIGKILL - 注意:如果
your_cmd是 shell 脚本且内部用&启了后台子进程,timeout默认只 kill 直接子进程(即 shell 解释器),后台子进程可能漏掉——这时加--foreground不解决,得靠脚本自身处理进程组
时间单位写错会导致行为完全失控
timeout 对单位极其敏感,不写单位 ≠ 默认秒,而是可能被解析为纳秒级整数(取决于底层实现),尤其在浮点数混用时容易出错。
- 错误写法:
timeout 0.5 your_cmd—— 实际可能被当作 0.5 纳秒,等效于立即超时,返回状态码 124 - 正确写法:
timeout 500ms your_cmd或timeout 0.5s your_cmd(必须带s) - 常见单位陷阱:
m是分钟,不是毫秒;ms才是毫秒(GNU coreutils ≥ 8.24 支持);5m= 5 分钟,5ms= 5 毫秒 - 跨平台兼容性:旧版 busybox 的
timeout不支持小数和ms,只认整数 +s/m/h/d,生产环境建议统一用整数秒
退出状态码不是 0 就代表失败,得看是不是超时
timeout 的退出码直接反映执行结果,但容易误判:
- 正常退出 → 返回子命令的原始退出码(如
ping失败返回 2,grep没匹配返回 1) - 超时退出 → 默认返回 124(不是 1 或 255)
- 用
--preserve-status后,超时也返回子命令原退出码(比如子命令本来就会返回 0,那超时后你也看到 0,无法区分成功/超时) - 如果用了
-s SIGKILL,超时后退出码是128 + 9 = 137;-s SIGINT则是128 + 2 = 130 - 自动化脚本中务必检查
$?是否等于 124,而不是只判断非零
复杂命令链要小心 shell 解析顺序
当你写 timeout 10s cmd1 | cmd2,timeout 只管控 cmd1,管道后续的 cmd2 不受限制,且 cmd1 被杀后 cmd2 可能因读取 EOF 继续运行或报错。
- 想限制整个管道,必须用
sh -c包一层:timeout 10s sh -c 'cmd1 | cmd2' - 如果命令含重定向或变量,同样需要
sh -c,否则timeout会把>、$VAR当作参数传给cmd,导致语法错误 - 权限问题:如需
sudo,sudo timeout ...是错的(sudo会尝试提权timeout自身);应写成timeout ... sudo cmd或sudo sh -c 'timeout ... cmd'
真正难的不是写对那行命令,而是预判信号能否抵达真正的 worker 进程——尤其是当命令启动了守护进程、用了 exec、或自己 fork 出独立进程组时,timeout 的默认行为大概率失效。











