如何将输出通过管道传输到另一个进程,但保留第一个进程的错误状态?

e.J*_*mes 11 command-line pipe io-redirection error-handling

可能的重复:
获取通过管道传输到另一个进程的退出代码

我正在使用以下命令行(在 makefile 中)通过 perl 脚本将来自编译器的详细错误消息通过管道传输,该脚本将它们简化为人类可读的内容:

g++ -c source.cpp -o source.o 2>&1 | perl /bin/gSTLFilt.pl
Run Code Online (Sandbox Code Playgroud)

不幸的是,这种方法“屏蔽”了g++命令返回的错误值。make 不知道该g++命令已失败,因为它返回的只是perl命令的错误结果。

有没有办法通过管道输出,并仍然保留原始错误条件?

如果它有所不同:我在运行 GNU bash 版本 2.04.0(1)-release (i686-pc- msys) 在 Windows XP 上。

Chr*_*own 12

我不确定 shellsh.exe提供了什么(因为有多个 shell 使用该名称作为其 Windows 可执行文件),但如果是bash或类似,您可以使用该$PIPESTATUS数组。对于您的示例,您将执行以下操作:

g++ -c source.cpp -o source.o 2>&1 | perl /bin/gSTLFilt.pl
echo "${PIPESTATUS[0]}"
Run Code Online (Sandbox Code Playgroud)


Kev*_*vin 9

Bash 有一个选项pipefail

The return status of a pipeline is the exit status of the last command,
unless  the  pipefail  option  is enabled.  If pipefail is enabled, the
pipeline's return status is the value of the last  (rightmost)  command
to  exit  with a non-zero status, or zero if all commands exit success-
fully.
Run Code Online (Sandbox Code Playgroud)

所以:

set -o pipefail && $GCC_COMMAND | $PERL_COMMAND
Run Code Online (Sandbox Code Playgroud)

Make 为每一行执行子shell 中的每一行,因此您需要将其添加到 gcc 行的开头。可能有一种方法可以让 make 只执行一个带有pipefailset 的命令,但我不知道。

尝试SHELL=/bin/bash在 Makefile 中添加(Make 应该使用这个

或尝试:

bash -o pipefail -c "$GCC_COMMAND | $PERL_COMMAND"
Run Code Online (Sandbox Code Playgroud)


Gil*_*il' 6

在传统 shell 中,管道中第一个命令的状态根本不会报告给脚本。只有最后一个命令的状态可用,在$?.

在 bash ?3.0 中,当您想要做的是在管道中的任何地方发生错误时停止,请使用pipefail选项

g++ -c source.cpp -o source.o 2>&1 | perl /bin/gSTLFilt.pl
Run Code Online (Sandbox Code Playgroud)

更一般地,在 bash 中,PIPESTATUS数组概括$?为涵盖最后一个管道中的所有命令。

$ (exit 1) | (exit 2) | (exit 3); echo ${PIPESTATUS[@]}
1 2 3
Run Code Online (Sandbox Code Playgroud)

Zsh 具有相同的功能,只是调用了数组pipestatus

% zsh -c '(exit 1) | (exit 2) | (exit 3); echo $pipestatus'    
1 2 3
Run Code Online (Sandbox Code Playgroud)

如果您愿意假设 bash(其中 IIRC 是 msys 提供的 shell 作为sh),那么您可以使用PIPESTATUS. 如果不是,您可以安排通过管道将退出状态传递到顶层 shell,并使过滤器程序退出,并在输入的最后一行读取状态,而不是将其用作正常输入。这很笨拙,但它可能很有用。

在 makefile 中,使用临时文件相对常见,这里将编译器消息视为另一个中间文件。

%.otmp %.g++-log: %.cpp
        g++ -c $< -o $@tmp 2>&1 >$*.g++-log
%.o: %.otmp %.g++-log
        perl /bin/gSTLFilt.pl <$*.g++-log
        mv $*.otmp $@
Run Code Online (Sandbox Code Playgroud)