![Bash 后台进程仍然阻止子 shell 中的脚本流](https://linux22.com/image/1494465/Bash%20%E5%90%8E%E5%8F%B0%E8%BF%9B%E7%A8%8B%E4%BB%8D%E7%84%B6%E9%98%BB%E6%AD%A2%E5%AD%90%20shell%20%E4%B8%AD%E7%9A%84%E8%84%9A%E6%9C%AC%E6%B5%81.png)
我正在尝试做一些涉及脚本调用子 shell 中的其他脚本来捕获它们的输出的事情。
其中一个脚本需要有启动后台进程的副作用。直接执行时,所有脚本都可以正常工作,但在子 shell 中调用时,脚本会阻塞。
作为一个独立的示例,请考虑以下两个脚本:
测试1
#!/bin/bash
echo $(./test2.sh)
测试2
#!/bin/bash
(yes > /dev/null ; echo 'yes killed') &
echo success
当我test2.sh
单独运行时,我得到了预期的结果“成功”在终端上,并yes
在后台运行。杀死yes
打印“是的,被杀了”按照预期运行到终端。
当我运行时,test1.sh
我期望得到相同的行为,但实际发生的是终端挂起,直到我终止yes
它,然后“成功是杀死”打印到终端。
我该对这些脚本进行哪些更改,以便通过调用其中任何一个脚本就能获得相同的行为?
这里的前提是,子 shell 评估实际上test1.sh
将存储在变量中以供以后使用。启动的后台进程test2.sh
应该在执行任一脚本后继续存在。
答案1
正如@choroba 和@GordonDavisson 所建议的,命令替换$( ... )
直到其所有标准输出断开连接后才会返回[1]。
这里的技巧是,即使你重定向所有命令的标准输出,子 shell 本身( ... )
仍然会附加其标准输出。这意味着以下内容将不起作用:
#!/bin/bash
(yes > /dev/null ; echo 'yes killed' > /dev/null) &
echo success
但这将:
#!/bin/bash
(yes ; echo 'yes killed' ) > /dev/null &
echo success
注意:您将不再获得任何像这样的终端或标准输出,但就我的目的而言,这不是问题。如果需要输出,您可以随时重定向到文件或其他内容
[1]另请参阅https://stackoverflow.com/questions/16874043/bash-command-substitution-forcing-process-to-foreground
答案2
命令替换$(...)
将输出转换为参数。它首先需要整个输出,因为不可能逐个动态地提供参数。
答案3
我认为 bash 的否认就是答案。请参阅help disown
。
使用方式如下:
yes &>/dev/null </dev/zero &
disown -h $!
重定向输出或关闭终端将导致 SIGHUP 信号或输出管道损坏(SIGPIPE),最终阻塞或终止程序。
如果你关心输出,请将其重定向到文件。或者使用禁止像这样:
nohup yes &
disown $!
一个好的解释nohup 和 disown。
答案4
你不需要 echo 来运行另一个 shell 脚本
#!/bin/bash
#echo $(./test2.sh)
./test2.sh