为何bash脚本启动的程序在脚本后台运行时会停止(仅读命名管道stdin时)
后台运行脚本时,从命名管道读取stdin的程序停止的问题分析与解决
行为差异解释
当bar.sh前台运行时:
cat > pipe作为前台进程可以正常读取终端stdin,命名管道的写端保持打开状态;foo.sh的stdin被重定向到管道,但它的代码逻辑中没有读取stdin的操作,因此不会触发任何终端相关的信号,持续循环打印"Running"。
当bar.sh后台运行时:
cat > pipe尝试读取控制终端的stdin,但后台进程组默认不允许读取终端输入,内核会向整个后台进程组(包含bar.sh、cat和foo.sh)发送SIGTTIN信号;SIGTTIN信号的默认行为是暂停整个进程组,因此foo.sh被暂停,打印操作停止。
如果不将foo.sh的stdin重定向到管道:
foo.sh不会参与到触发SIGTTIN的进程组逻辑中,后台运行时不会收到该信号,因此可以持续正常运行。
解决方法
要让foo.sh在bar.sh后台运行时保持运行,核心是避免cat尝试读取终端stdin,从而防止SIGTTIN信号触发。以下是几种可行方案:
方案1:修改bar.sh,用轻量方式保持管道写端打开
替换依赖终端输入的cat命令,用bash内置命令: 保持管道写端打开,避免触碰终端stdin:
#!/bin/bash rm -f pipe mkfifo pipe ./foo.sh < pipe & # 用内置命令保持管道写端打开,无需读取终端输入 : > pipe & # 可选:等待后台进程,防止脚本提前退出导致管道写端关闭 wait
方案2:运行bar.sh时重定向其stdin到/dev/null
无需修改脚本,直接在运行时将脚本的stdin指向空设备,让cat从空设备读取而非终端:
./bar.sh < /dev/null &
方案3:用持续后台进程保持管道写端打开
使用tail -f /dev/null这类不依赖终端输入的进程,长期保持管道写端打开:
#!/bin/bash rm -f pipe mkfifo pipe ./foo.sh < pipe & tail -f /dev/null > pipe & wait
内容的提问来源于stack exchange,提问作者Eclogite
相关产品推荐
相关产品推荐

