Alpine中stdout接管道、stderr到/dev/null仍输出Terminated问题
问题描述
- 编写Shell脚本实现目标URL网络延迟检测,原始代码如下:
latency=$(timeout 10 ping -4 -qc5 $url 2>&1 | awk -F/ '/^(rtt|round-trip)/ { printf "%.1f", $5 } END{ if(!NR) print "busy" }')
- 在Alpine Linux环境执行时,若
timeout触发超时终止进程,控制台会额外输出Terminated提示。通过设置极短超时时长可稳定复现问题,复现命令与输出如下:
$ timeout 1 ping -4 -c5 $url 2>&1 | awk -F/ '/^(rtt|round-trip)/ { printf "%.1f", $5 } END{ if(!NR) print "busy" }' Terminated
- 单独执行命令时,将stderr重定向到
/dev/null可屏蔽该提示,测试结果如下:
$ timeout 2 sleep 5 2>/dev/null $
- 尝试将「stdout导入管道、stderr重定向到/dev/null」的逻辑应用到原管道命令后,
Terminated提示依然存在,执行命令如下:
$ timeout 1 ping -4 -c5 $url 2>/dev/null | awk -F/ '/^(rtt|round-trip)/ { printf "%.1f", $5 } END{ if(!NR) print "busy" }' Terminated
错误原因
- 之前添加的
2>/dev/null仅重定向了ping进程的错误输出,没有覆盖整个管道链路的错误输出通道。 - 这个
Terminated提示并不是timeout或ping打印的,是当前交互Shell输出的:管道中的每个命令都会作为独立进程被Shell等待,当timeout超时后向ping发送终止信号,ping被信号杀死退出,交互Shell捕获到这个被信号终止的进程状态后,会主动向自身的stderr打印终止提示,这个输出流既不属于ping也不属于timeout,给单个命令加stderr重定向自然无法拦截。 - 单独执行
timeout 2 sleep 5 2>/dev/null时没有提示,是因为Shell只需要等待timeout这一个前台进程,timeout发送信号终止sleep后自身是正常退出的,Shell捕获到timeout的正常退出状态就不会打印提示。
解决方案
直接通过命令分组把整个管道逻辑包裹,统一将分组内所有进程的stderr(包括Shell打印的终止提示)重定向到/dev/null即可,修正后的代码如下:
latency=$({ timeout 10 ping -4 -qc5 "$url" 2>&1 | awk -F/ '/^(rtt|round-trip)/ { printf "%.1f", $5 } END{ if(!NR) print "busy" }'; } 2>/dev/null)
补充说明:
- 给
$url变量增加双引号包裹,避免URL中存在特殊字符时引发命令解析错误。 - 如果是将逻辑写入Shell脚本文件执行(非交互Shell环境),非交互Shell默认不会打印进程被信号终止的提示,即使不加重定向也不会出现
Terminated输出,该问题仅在命令行直接交互执行时触发。
内容的提问来源于stack exchange,提问作者Liso
相关产品推荐
相关产品推荐

