Bash 后台进程仍然阻止子 shell 中的脚本流

Bash 后台进程仍然阻止子 shell 中的脚本流

我正在尝试做一些涉及脚本调用子 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

相关内容