jua*_*eon 27 bash process-substitution
我希望我的 shell 脚本在使用它们执行的命令失败时失败。
通常我这样做:
set -e
set -o pipefail
Run Code Online (Sandbox Code Playgroud)
(通常我set -u
也会添加)
问题是上述方法都不适用于进程替换。此代码打印“ok”并以返回代码 = 0 退出,而我希望它失败:
#!/bin/bash -e
set -o pipefail
cat <(false) <(echo ok)
Run Code Online (Sandbox Code Playgroud)
除了进程替换之外,是否有任何等同于“pipefail”的东西?有没有其他方法可以将命令的输出作为文件传递给命令,但只要这些程序中的任何一个失败就会引发错误?
一个穷人的解决方案是检测这些命令是否写入 stderr(但某些命令在成功的情况下写入 stderr)。
另一个更符合 posix 的解决方案是使用命名管道,但我需要将那些使用进程替换的命令作为从编译代码动态构建的单行程序启动,并且创建命名管道会使事情复杂化(额外的命令,捕获错误删除它们等)
您只能解决该问题,例如:
cat <(false || kill $$) <(echo ok)
other_command
Run Code Online (Sandbox Code Playgroud)
在SIGTERM
可以执行第二个命令之前,脚本的子 shell 是d ( other_command
)。该echo ok
命令“有时”执行:问题在于进程替换是异步的。不能保证该kill $$
命令在该命令之前或之后echo ok
执行。这是操作系统调度的问题。
考虑这样的 bash 脚本:
#!/bin/bash
set -e
set -o pipefail
cat <(echo pre) <(false || kill $$) <(echo post)
echo "you will never see this"
Run Code Online (Sandbox Code Playgroud)
该脚本的输出可以是:
$ ./script
Terminated
$ echo $?
143 # it's 128 + 15 (signal number of SIGTERM)
Run Code Online (Sandbox Code Playgroud)
或者:
$ ./script
Terminated
$ pre
post
$ echo $?
143
Run Code Online (Sandbox Code Playgroud)
您可以尝试一下,尝试几次后,您将在输出中看到两种不同的顺序。在第一个中,脚本在其他两个echo
命令可以写入文件描述符之前终止。在第二个中,false
或kill
命令可能是在echo
命令之后安排的。
或者更准确地说:将信号发送到 shell 进程signal()
的kill
实用程序的系统调用SIGTERM
被安排(或交付)晚于或早于 echowrite()
系统调用。
但是,脚本停止并且退出代码不是 0。因此它应该可以解决您的问题。
当然,另一个解决方案是为此使用命名管道。但是,这取决于您的脚本实现命名管道或上述解决方法的复杂程度。
参考:
作为记录,即使答案和评论是好的和有帮助的,我也结束了实现一些不同的东西(我在父进程中接收信号有一些限制,我没有在问题中提到)
基本上,我结束了做这样的事情:
command <(subcomand 2>error_file && rm error_file) <(....) ...
Run Code Online (Sandbox Code Playgroud)
然后我检查错误文件。如果它存在,我知道哪个子命令失败了(并且 error_file 的内容可能很有用)。我最初想要的更冗长和骇人听闻,但比在单行 bash 命令中创建命名管道要简单得多。