是的,bash就像ksh(功能来自何处)一样,不等待进程替换内的进程(在脚本中运行下一个命令之前)。
对于<(...)一个,通常如下所示:
cmd1 <(cmd2)
外壳程序将等待,cmd1并且cmd1通常会cmd2由于其读取而等待,直到被替换的管道上的文件结束为止,并且文件结束通常在cmd2死时发生。这就是几个shell(非bash)不用费心等待cmd2in 的相同原因cmd2 | cmd1。
对于cmd1 >(cmd2),然而,这通常不是这样的,因为它更cmd2通常等待cmd1一般会退出后,有那么。
这是固定的,zsh因为它在cmd2那里等待(但是如果您将其编写为cmd1 > >(cmd2)且cmd1不是内置的,则不行,{cmd1} > >(cmd2)而是按记录使用)。
ksh默认情况下不等待,但是让您使用wait内置命令等待它(它也使pid在中可用$!,尽管这样做对您没有帮助cmd1 >(cmd2) >(cmd3))
rc(使用cmd1 >{cmd2}语法),ksh除了可以使用获取所有后台进程的pids之外$apids。
es(也cmd1 >{cmd2})等待cmd2像zsh,并且还等待cmd2在<{cmd2}过程重定向。
bash确实在中提供了的pid cmd2(或更确切地说,是在该子shell cmd2的子进程中运行的pid,即使它是那里的最后一个命令)$!,但不允许您等待它。
如果必须使用bash,则可以通过使用以下命令来等待两个命令来解决此问题:
{ { cmd1 >(cmd2); } 3>&1 >&4 4>&- | cat; } 4>&1
这使两者cmd1并cmd2使其fd 3通向管道。cat将等待最终的文件在另一端,所以通常只退出时都cmd1和cmd2都死了。Shell将等待该cat命令。您可能会看到它是一个捕获所有后台进程终止的网络(您可以将其用于在后台启动的其他事情,例如with &,coprocs甚至是后台自身的命令,只要它们不像守护进程那样关闭所有文件描述符)。
请注意,由于上面提到的浪费的subshell进程,即使cmd2关闭fd 3也可以运行(命令通常不执行此操作,但有些命令喜欢sudo或ssh执行)。将来的版本bash可能最终会像其他Shell一样在那里进行优化。然后,您将需要以下内容:
{ { cmd1 >(sudo cmd2; exit); } 3>&1 >&4 4>&- | cat; } 4>&1
为了确保在打开fd 3时还有一个额外的Shell进程,等待该sudo命令。
请注意,它cat不会读取任何内容(因为进程不会在其fd 3上进行写入)。它只是用于同步。它只会执行一个read()系统调用,最后将不返回任何内容。
您实际上可以cat通过使用命令替换执行管道同步来避免运行:
{ unused=$( { cmd1 >(cmd2); } 3>&1 >&4 4>&-); } 4>&1
这一次,它的外壳,而不是cat说从它的另一端是FD 3的开管道读取cmd1和cmd2。我们使用的是变量分配,因此中的退出状态cmd1可用$?。
或者,您可以手动进行流程替换,然后甚至可以使用系统的sh语法,因为这将成为标准的Shell语法:
{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1
尽管请注意,如前所述,并非所有sh实现都将cmd1在cmd2完成后等待(尽管比其他方法要好)。当时,$?包含的退出状态cmd2;不过bash,zsh并使cmd1的退出状态分别在${PIPESTATUS[0]}和中可用$pipestatus[1](另请参见pipefail一些shell中的选项,因此$?可以报告除最后一个管道组件之外的管道组件的故障)
请注意,yash其过程重定向功能也有类似的问题。cmd1 >(cmd2)将被写cmd1 /dev/fd/3 3>(cmd2)在那里。但是cmd2不等待,您也不能wait等待它,并且它的pid也不在$!变量中可用。您将使用与相同的解决方法bash。