从Perl中的STDIN捕获退出状态

zig*_*don 2 perl pipe exit-code

我有一个使用如下命令运行的perl脚本:

/path/to/binary/executable | /path/to/perl/script.pl
Run Code Online (Sandbox Code Playgroud)

该脚本对二进制文件的输出执行有用的操作,然后在STDIN用完后退出(<>返回undef).除非二进制文件以非零代码退出,否则这一切都很好.从脚本的POV开始,它认为脚本刚刚结束,所以它清理并退出,代码为0.

有没有办法让perl脚本看到退出代码是什么?理想情况下,我想要这样的东西工作:

# close STDIN, and if there was an error, exit with that same error.
unless (close STDIN) {
   print "error closing STDIN: $! ($?)\n";
   exit $?;
}
Run Code Online (Sandbox Code Playgroud)

但不幸的是,这似乎不起作用:

$ (date; sleep 3; date; exit 1) | /path/to/perl/script.pl /tmp/test.out
Mon Jun  7 14:43:49 PDT 2010
Mon Jun  7 14:43:52 PDT 2010
$ echo $?
0
Run Code Online (Sandbox Code Playgroud)

有没有办法让它做我的意思?

编辑添加:

perl脚本实时操作二进制命令的输出,因此将其全部缓冲到文件中是不可行的解决方案.但是,它不需要知道退出代码,直到脚本结束.

int*_*ted 8

bash环境变量$PIPESTATUS是一个数组,它包含最后一个命令管道的每个部分的状态.例如:

$ false | true; echo "PIPESTATUS: ${PIPESTATUS[@]};  ?: $?"
PIPESTATUS: 1 0;  ?: 0
Run Code Online (Sandbox Code Playgroud)

所以听起来不是重构你的perl脚本,你只需要运行该管道命令的脚本来检查$PIPESTATUS.在$PIPESTATUS没有的情况下使用[@]会给出数组第一个元素的值.

如果需要检查初始可执行文件和perl脚本的状态,则需要先将$ PIPESTATUS分配给另一个变量:

status=(${PIPESTATUS[@]})
Run Code Online (Sandbox Code Playgroud)

然后你可以单独检查它们,比如

if (( ${status[0]} )); then echo "main reactor core breach!"; exit 1;
elif (( ${status[1]} )); then echo "perls poisoned by toxic spill!"; exit 2;
fi;
Run Code Online (Sandbox Code Playgroud)

您必须通过临时变量执行此操作,因为下一个语句(即使它是一个if语句)将${PIPESTATUS[@]}在以下语句之前重置,即使它是一个elif语句,也可以检查它.

请注意,这些东西仅适用于bash,而不适用于原始的bourne shell(通常sh,虽然许多系统由于其向后兼容性而链接/bin/sh/bin/bash).所以如果你把它放在shell脚本中,第一行应该是

#!/bin/bash
Run Code Online (Sandbox Code Playgroud)

而不是#!/bin/sh.