我有两个通过管道连接的进程foobar

$ foo | bar


bar始终退出0;我对foo的退出代码感兴趣。有什么办法吗?

评论

stackoverflow.com/questions/1221833/…

#1 楼

bashzsh具有一个数组变量,该变量保存由外壳程序执行的最后一个管道的每个元素(命令)的退出状态。索引从零开始:
$ false | true
$ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
1 0

如果您使用的是bash,则该数组称为PIPESTATUS(情况很重要!),数组索引从1开始:
$ false | true
$ echo "${pipestatus[1]} ${pipestatus[2]}"
1 0

将它们组合在一个函数中以不会丢失值的方式进行操作:仅设置zshpipestatus之一。另一个将为空白。这将使函数以bash结尾(请注意没有引号!)。

评论


和zstat中的pipestatus。不幸的是,其他外壳没有此功能。

–吉尔斯'所以-不再是邪恶的'
2011年6月2日21:05

注意:zsh中的数组以与直觉相反的方式从索引1开始,因此它回显“ $ pipestatus [1]”“ $ pipestatus [2]”。

– Christophh Wurm
2011年11月14日14:09



您可以像这样检查整个管道:if [`echo“ $ {PIPESTATUS [@]}” | tr -s''| bc` -ne 0];然后回显失败;科幻

–l0b0
2012-09-25 15:39



@JanHudec:也许您应该阅读我答案的前五个字。还请指出问题在哪里要求仅POSIX答案。

– camh
2014年12月16日上午8:27

@ JanHudec:它也没有标记POSIX。您为什么认为答案必须是POSIX?没有指定,所以我提供了一个合格的答案。我的答案没有什么不妥,另外还有多个答案可以解决其他情况。

– camh
2014年12月17日在9:15

#2 楼

共有3种常见的方法:

Pipefail

第一种方法是设置pipefail选项(kshzshbash)。这是最简单的方法,它的基本操作是将退出状态$?设置为最后一个程序的退出代码,以退出非零值(如果成功退出,则返回零)。

$ false | true; echo $?
0
$ set -o pipefail
$ false | true; echo $?
1


$ PIPESTATUS

Bash还具有一个名为$PIPESTATUS(在$pipestatus中为zsh)的数组变量,它包含最后一个管道中所有程序的退出状态。

$ true | true; echo "${PIPESTATUS[@]}"
0 0
$ false | true; echo "${PIPESTATUS[@]}"
1 0
$ false | true; echo "${PIPESTATUS[0]}"
1
$ true | false; echo "${PIPESTATUS[@]}"
0 1


您可以使用第三个命令示例来获取所需的管道中的特定值。

单独执行

这是最笨拙的解决方案。分别运行每个命令并捕获状态

$ OUTPUT="$(echo foo)"
$ STATUS_ECHO="$?"
$ printf '%s' "$OUTPUT" | grep -iq "bar"
$ STATUS_GREP="$?"
$ echo "$STATUS_ECHO $STATUS_GREP"
0 1


评论


该死!我只是要发布关于PIPESTATUS的信息。

–slm♦
13年4月21日在13:57

作为参考,在此SO问题中讨论了其他几种技术:stackoverflow.com/questions/1221833/…

–slm♦
13年4月21日在14:00

@Patrick pipestatus解决方案可在bash上使用,只是在我使用ksh脚本的情况下可以解决,您认为我们可以找到类似于pipestatus的东西? ,(与此同时,我看到ksh不支持pipestatus)

– Yael
13年4月21日在14:32



@yael我不使用ksh,但是从它的联机帮助页面看,它不支持$ PIPESTATUS或任何类似的东西。它确实支持pipefail选项。

–干粉
13年4月21日在15:30

我决定使用pipefail,因为它允许我在此处获取失败命令的状态:LOG = $(failed_command | success_command)

–vmrob
2014年2月8日在9:20

#3 楼

此解决方案无需使用bash特定功能或临时文件即可工作。奖励:最后,退出状态实际上是退出状态,而不是文件中的某些字符串。

情况:

someprog | filter


someprog的退出状态和filter的输出。

这是我的解决方法:


此构造还可以与简单的命令分组filter而不是子外壳someprog一起使用。子外壳会带来一些影响,其中包括性能成本,在这里我们不需要。请阅读精美的bash手册以了解更多详细信息:https://www.gnu.org/software/bash/manual/html_node/Command-Grouping.html

((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1


bash语法要求花括号使用空格和分号,以使结构变得更加宽敞。在本文的其余部分中,我将使用subshel​​l变体。


示例{...}(...)

{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; } } 4>&1


示例输出:

someprog() {
  echo "line1"
  echo "line2"
  echo "line3"
  return 42
}

filter() {
  while read line; do
    echo "filtered $line"
  done
}

((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1

echo $?



注意:子进程从父进程继承打开文件描述符。这意味着someprog将继承打开的文件描述符3和4。如果filter写入文件描述符3,则它将变为退出状态。真正的退出状态将被忽略,因为someprog只读取一次。

如果您担心someprog可能写入文件描述符3或4,那么最好在调用read之前关闭文件描述符。 />
filtered line1
filtered line2
filtered line3
42


someprog之前的someprog在执行exec 3>&- 4>&-之前关闭文件描述符,因此对于someprog那些文件描述符根本不存在。

也可以编写像这样:someprog


构造的逐步说明:

(((((exec 3>&- 4>&-; someprog); echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1


从下至上:


创建一个子shell,并将文件描述符4重定向到stdout。这意味着在子shell中打印到文件描述符4的所有内容最终都将作为整个构造的标准输出。
创建了一个管道,并执行了左侧(someprog)和右侧(someprog 3>&- 4>&-)的命令。 #part3也是管道的最后一个命令,这意味着stdin中的字符串将成为整个构造的退出状态。
创建了一个子shell,并将文件描述符3重定向到stdout。这意味着在此子shell中打印到文件描述符3的所有内容都将在#part2中结束,进而将成为整个构造的退出状态。
创建了一个管道,并在左侧创建了命令(exit $xs#part2)和右(#part5)被执行。 #part6的输出重定向到文件描述符4。在filter >&4中,文件描述符4重定向到stdout。这意味着filter的输出是整个结构的标准输出。
来自#part1的退出状态被打印到文件描述符3。在filter中,文件描述符3重定向到#part6。这意味着从#part3退出的状态将是整个结构的最终退出状态。

#part2被执行。退出状态在#part6中获取。标准输出由someprog中的管道获取,并转发到#part5#part4的输出将依次达到stdout,如filter中所述



评论


真好对于我可能会做的功能(读取;退出$ REPLY)

– jthill
16-3-2在1:03



(exec 3>&-4>&-; someprog)简化为someprog 3>&-4>&-。

–罗杰·佩特
17年4月13日13:33

此方法也可以不使用子shell:{{{{someprog 3>&-4>&-;回声$? >&3; } |过滤器>&4; } 3>&1; } | {阅读xs;退出$ xs; }; } 4>&1

– josch
19年2月28日在13:43

#4 楼

虽然不是您要求的完全正确,但可以使用

#!/bin/bash -o pipefail


,以便管道返回最后一个非零的返回值。

可能有点更少的编码

编辑:示例

[root@localhost ~]# false | true
[root@localhost ~]# echo $?
0
[root@localhost ~]# set -o pipefail
[root@localhost ~]# false | true
[root@localhost ~]# echo $?
1


评论


脚本内的set -o pipefail应该更健壮,例如万一有人通过bash foo.sh执行脚本。

–maxschlepzig
2011年6月3日,9:17

这是如何运作的?你有例子吗?

–约翰
2011年6月8日18:40

请注意,-o pipefail不在POSIX中。

– scy
2013年1月25日15:15

这在我的BASH 3.2.25(1)-发行版中不起作用。在/ tmp / ff的顶部,我有#!/ bin / bash -o pipefail。错误是:/ bin / bash:第0行:/ bin / bash:/ tmp / ff:无效的选项名称

–Felipe Alvarez
2014年3月24日在6:01

@FelipeAlvarez:某些环境(包括linux)无法解析#上的空格!行超出第一个,因此变成/ bin / bash -o pipefail / tmp / ff,而不是必需的/ bin / bash -o pipefail / tmp / ff-使用optarg解析getopt(或类似方法),其中是ARGV中的下一项,作为-o的参数,因此失败。如果要进行包装(例如,执行bash-pf只是执行exec / bin / bash -o pipefail“ $ @”,然后将其放在#!行上,那将起作用。另请参见:en.wikipedia.org / wiki / Shebang_%28Unix%29

– lindes
2015年12月5日,0:09

#5 楼

我可能会做的是将foo的退出代码输入bar。例如,如果我知道foo永远不会产生仅包含数字的行,那么我可以添加退出代码:

{ foo; echo "$?"; } | awk '!/[^0-9]/ {exit(
{ foo; echo .; echo "$?"; } | awk '/^\.$/ {getline; exit(
exit_codes=$({ { foo; echo foo:"$?" >&3; } |
               { bar >/dev/null; echo bar:"$?" >&3; }
             } 3>&1)
)} {…}'
)} {…}'


或者如果我知道输出来自foo的行永远不会只包含.的行:

如果bar是不需要输出的复杂管道,则可以通过在不同的文件描述符上打印退出代码来绕过它的一部分。

output=$(echo;
         { { foo; echo foo:"$?" >&3; } |
           { bar | sed 's/^/^/'; echo bar:"$?" >&3; }
         } 3>&1)
nl='
'
foo_exit_code=${output#*${nl}foo:}; foo_exit_code=${foo_exit_code%%$nl*}
bar_exit_code=${output#*${nl}bar:}; bar_exit_code=${bar_exit_code%%$nl*}
output=$(printf %s "$output" | sed -n 's/^\^//p')


在此之后,通常是bar,但是如果$exit_codes在读取所有输入之前退出或者不是很幸运,则可能是foo:X bar:Y。我认为在所有unices上写入最多512字节的管道都是原子的,因此只要标签字符串在507个字节以下,bar:Y foo:Xbar的部分就不会混在一起。

如果需要从foo:$?捕获输出,这变得很困难。您可以通过安排bar:$?的输出从不包含看起来像退出代码指示的行来组合上述技术,但是确实会变得很奇怪。

foo_ret_file=$(mktemp -t)
{ foo; echo "$?" >"$foo_ret_file"; } | bar
bar_ret=$?
foo_ret=$(cat "$foo_ret_file"; rm -f "$foo_ret_file")


,当然,有一个使用临时文件存储状态的简单选项。很简单,但是在生产中却不是那么简单:


如果同时运行多个脚本,或者同一脚本在多个地方使用此方法,则需要确保它们使用不同的临时脚本。文件名。
很难在共享目录中安全地创建临时文件。通常,bar是确保脚本能够写入文件的唯一位置。使用bar,它不是POSIX,但现在在所有严重的unice上都可用。

q4312078q

评论


当使用临时文件方法时,我更喜欢为EXIT添加一个陷阱,该陷阱会删除所有临时文件,以便即使脚本死了也不会留下垃圾

– miracle173
13-10-16在20:16

#6 楼

从管道开始:

foo | bar | baz


这里是一个通用解决方案,仅使用POSIX shell,没有临时文件:

exec 4>&1
error_statuses="`((foo || echo "0:$?" >&3) |
        (bar || echo "1:$?" >&3) | 
        (baz || echo "2:$?" >&3)) 3>&1 >&4`"
exec 4>&-


$error_statuses随机包含任何失败进程的状态码,并带有索引来指示哪个命令发出了每种状态。

# if "bar" failed, output its status:
echo "$error_statuses" | grep '1:' | cut -d: -f2

# test if all commands succeeded:
test -z "$error_statuses"

# test if the last command succeeded:
! echo "$error_statuses" | grep '2:' >/dev/null


请注意在$error_statuses周围的引号我的测试;没有它们grep无法区分,因为换行符被强制为空格。

#7 楼

如果您安装了moreutils软件包,则可以使用mispipe实用程序,它完全符合您的要求。

#8 楼

所以我想像lesmana一样回答,但是我认为我的方法可能更简单一些,并且比纯Bourne-shell更有优势:

# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1's exit status.


我认为这最好从内到外进行解释– command1将在stdout(文件描述符1)上执行并打印其常规输出,然后完成后,printf将在stdout上执行并打印command1的退出代码,但是该stdout重定向到文件描述符3 。

command1正在运行时,其stdout正在通过管道传递给command2(printf的输出从不将其传递给command2,因为我们将其发送给文件描述符3而不是管道读取的1)。然后,我们将command2的输出重定向到文件描述符4,以便它也不会出现在文件描述符1中-因为我们希望稍后释放文件描述符1,因为我们会将文件描述符3的printf输出放回到文件描述符中1 –因为这就是命令替换(反引号)将捕获的内容,并将其放入变量中。打开文件描述符4作为外壳程序标准输出的副本。从命令内部的角度来看,命令替换将捕获在标准上写的任何内容–但是,由于就命令替换而言,command2的输出将进入文件描述符4,因此命令替换不会捕获它–但是一旦“退出”命令替换,它实际上仍将转到脚本的整体文件描述符1。

exec 4>&1必须是单独的命令,因为许多常见的shell都不喜欢它当您尝试在命令替换中写入文件描述符时,该命令在使用替换的“外部”命令中打开。因此,这是最简单的可移植方式。)

您可以用一种不太技术性且更有趣的方式来查看它,就像命令的输出彼此跳跃一样:command1通过管道传递到command2,然后printf的输出会跳过命令2,以便command2不会捕获它,然后命令2的输出跳出命令替换,就像printf恰好及时被替换捕获那样,以便它最终出现在变量中,而命令2的输出以一种很快乐的方式写入标准输出,就像另外,据我了解,exec 4>&1仍将包含管道中第二个命令的返回代码,因为变量赋值,命令替换和复合命令对于它们内部的命令的返回代码,因此应该传播command2的返回状态–且不必定义其他功能,这就是为什么我认为这可能比lesmana提出的解决方案更好。

lesmana提出的警告,command1可能会在某个时候使用文件描述符3或4结束,因此,为了更可靠,您可以这样做:

请注意,我在示例中使用了复合命令,但是子外壳程序(使用$?代替( )也会起作用,尽管可能效率较低。)

命令继承文件描述符从启动它们的过程开始,因此整行第二行将继承文件描述符4,并且复合命令后跟{ }将继承文件描述符3。因此,3>&1确保内部复合命令不会继承文件描述符四个,而4>&-将不会继承文件描述符三个,因此command1获得了一个“更干净”的更标准的环境。您也可以将内部3>&-移至4>&-旁边,但是我认为为什么不尽可能限制其范围。

我不确定事情多久直接使用文件描述符3和4 –我认为大多数时候程序都使用syscall来返回当前未使用的文件描述符,但有时代码会直接写入文件描述符3猜测(我可以想象一个程序检查文件描述符以查看它是否打开,如果打开则使用它,或者如果没有打开则相应地表现不同)。因此,最好记住后一种情况,并在通用情况下使用。

评论


看起来很有趣,但是我不太清楚您希望该命令做什么,而我的计算机也不能。我得到-bash:3:错误的文件描述符。

– G-Man说“恢复莫妮卡”
2015年6月5日7:03

@ G-Man是的,我一直忘记bash并不了解文件描述符,这与我通常使用的shell(busybox附带的ash)不同。当我想到使bash满意的解决方法时,我会通知您。同时,如果您有方便使用的debian盒子,则可以使用破折号尝试,如果您有方便的busybox,则可以使用busybox ash / sh进行尝试。

–mtraceur
2015年6月5日,12:09

@ G-Man关于我期望命令执行的操作,以及它在其他shell中的操作,是从command1重定向stdout,这样它就不会被命令替换捕获,但是一旦在命令替换之外,它就会掉线fd3返回到stdout,以便按预期方式将其通过管道传递到command2。当command1退出时,printf触发并打印其退出状态,该退出状态通过命令替换捕获到变量中。此处非常详细的细分:stackoverflow.com/questions/985876/tee-and-exit-status/…另外,您的评论似乎是在侮辱您?

–mtraceur
2015年6月5日,12:17

我应该从哪里开始? (1)对不起,如果您感到侮辱。 “看起来很有趣”的意思是认真的。如果能够像您期望的那样紧凑地工作,那就太好了。除此之外,我只是在说,我不明白您的解决方案应该做什么。我已经在Unix上工作了很长时间(因为Linux诞生之前),并且,如果我不了解某些内容,那是一个危险信号,也许其他人也不会理解它,并且它需要更多说明(IMNSHO)。 …(续)

– G-Man说“恢复莫妮卡”
2015年6月6日,凌晨3:14

(续)…由于您“……想…………[您]比普通人更了解一切”,也许您应该记住,Stack Exchange的目标不是成为命令编写服务,因此提供数千种一次性解决方案,以解决琐碎的问题;而是教人们如何解决自己的问题。而且,为此,也许您需要充分解释一些内容,以使“普通人”能够理解。看看lesmana的答案来举例说明。 …(续)

– G-Man说“恢复莫妮卡”
2015年6月6日,凌晨3:15

#9 楼

lesmana的上述解决方案也可以通过使用{ .. }来完成,而无需启动嵌套子进程的开销(记住,这种形式的分组命令总是必须以分号结尾)。这样的事情:

{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | stdintoexitstatus; } 4>&1


我已经用破折号版本0.5.5和bash版本3.2.25和4.2.42检查了此结构,即使某些外壳没有不支持{ .. }分组,但仍符合POSIX。

评论


这对我尝试过的大多数shell都非常有效,包括NetBSD sh,pdksh,mksh,破折号,bash。但是,即使在ksh中使用set -o pipefail或在其中任意一个洒水的等待命令中,我也无法使其与AT&T Ksh(93s +,93u +)或zsh(4.3.9,5.2)一起使用。我认为,至少在某种程度上,这可能是ksh的解析问题,就像我坚持使用subshel​​l一样,它仍然可以正常工作,但是即使为ksh选择subshel​​l变体,但将复合命令留给其他人使用,失败。

– Greg A. Woods
17年8月3日,0:42

#10 楼

如果您无法使用一种常见的解决方案,则以下内容是@Patrik答案的附加内容。

此答案假定为以下内容:


您有一个不了解$PIPESTATUSset -o pipefail的外壳

您想使用管道进行并行执行,因此没有临时文件。
您不希望周围出现其他混乱情况如果您可能由于突然断电而中断了脚本。
此解决方案应该相对易于遵循并且易于阅读。
您不想引入其他子外壳。
您不能摆弄。使用现有的文件描述符,因此切勿触动stdin / out / err(但是您可以暂时引入一些新的文件)。


其他假设。您可以摆脱所有方法,但是这会使配方变得太多,因此这里不介绍它:


您所要知道的是PIPE中的所有命令都具有退出代码0 。
不需要其他边带信息。
您的外壳确实等待所有管道命令返回。


在此之前:foo | bar | baz但是这仅返回最后一个命令的退出代码(baz

想要:如果管道中的任何命令失败,则$?一定不能为0(true)

之后:

TMPRESULTS="`mktemp`"
{
rm -f "$TMPRESULTS"

{ foo || echo $? >&9; } |
{ bar || echo $? >&9; } |
{ baz || echo $? >&9; }
#wait
! read TMPRESULTS <&8
} 9>>"$TMPRESULTS" 8<"$TMPRESULTS"

# $? now is 0 only if all commands had exit code 0


说明:


使用mktemp创建一个临时文件。通常,这会立即在/tmp中创建一个文件,然后将该临时文件重定向到FD 9进行写入,并重定向到FD 8进行读取
,然后立即删除该临时文件。但是,它保持打开状态,直到两个FD都不存在为止。
现在开始管道。如果有错误,则每个步骤仅添加到FD9。
wait需要ksh,因为ksh否则不等待所有管道命令完成。但是请注意,如果存在一些后台任务,则会有不希望的副作用,因此默认情况下我将其注释掉。如果等待没有影响,可以对其进行注释。
之后,将读取文件的内容。如果为空(因为所有工作正常),则read返回false,因此true指示错误

这可以用作单个命令的插件替换,并且仅需要执行以下操作:


未使用的FD 9和8
用于保存临时文件名称的单个环境变量
并且此配方可以适应几乎所有允许IO重定向的shell
它与平台无关,不需要/proc/fd/N之类的东西


错误:

该脚本有一个bug,以防/tmp空间不足。如果您也需要保护以防这种人为的情况,则可以执行以下操作,但是这样做的缺点是,0中的000的数量取决于管道中的命令数量,因此稍微复杂一些:

TMPRESULTS="`mktemp`"
{
rm -f "$TMPRESULTS"

{ foo; printf "%1s" "$?" >&9; } |
{ bar; printf "%1s" "$?" >&9; } |
{ baz; printf "%1s" "$?" >&9; }
#wait
read TMPRESULTS <&8
[ 000 = "$TMPRESULTS" ]
} 9>>"$TMPRESULTS" 8<"$TMPRESULTS"


可移植性说明:


ksh和仅等待最后一个管道命令的类似外壳需要不注释wait
最后一个示例使用printf "%1s" "$?"而不是echo -n "$?",因为它更便于携带。并非每个平台都能正确解释-n
printf "$?"也会做到这一点,但是printf "%1s"会遇到一些极端情况,以防您在某个真正损坏的平台上运行脚本。 (阅读:如果您恰巧在paranoia_mode=extreme中进行编程。)
在支持多位数字的平台上,FD 8和FD 9可能更高。符合POSIX标准的Shell只需支持一个数字即可。
已在Debian 8.2 shbashkshashsash甚至csh上进行了测试


#11 楼

这是可移植的,即可以与任何POSIX兼容的外壳一起使用,不需要当前目录可写,并且允许使用相同技巧的多个脚本同时运行。

(foo;echo $?>/tmp/_$$)|(bar;exit $(cat /tmp/_$$;rm /tmp/_$$))

>编辑:
这是继吉尔斯的评论后的更强版本: :

(s=/tmp/.$$_$RANDOM;((foo;echo $?>$s)|(bar)); exit $(cat $s;rm $s))


评论


由于某些原因,这不起作用。 1.临时文件可以在写入之前被读取。 2.在共享目录中使用可预测的名称创建临时文件是不安全的(琐碎的DoS,符号链接争用)。 3.如果同一脚本多次使用此技巧,则它将始终使用相同的文件名。解决方法1,在管道完成后读取文件。要解决2和3,请使用具有随机生成名称的临时文件或位于私有目录中。

–吉尔斯'所以-不再是邪恶的'
2011年6月8日23:00

+1好$ {PIPESTATUS [0]}比较容易,但是只要知道Gilles提到的问题,这里的基本思想就可以奏效。

–约翰
2011年6月9日下午6:36

您可以保存一些子shell:(s = / tmp /.$$_$ RANDOM; {foo; echo $?> $ s;} | bar;退出$(cat $ s; rm $ s))。 @Johan:我同意使用Bash会更容易,但是在某些情况下,知道如何避免Bash是值得的。

– dubiousjim
2012年8月29日在22:25



#12 楼

在采取一些预防措施的情况下,此方法应该起作用:

foo-status=$(mktemp -t)
(foo; echo $? >$foo-status) | bar
foo_status=$(cat $foo-status)


评论


像jlliagre一样如何清理?您是否不将名为foo-status的文件留下?

–约翰
2011年6月8日在18:39

@Johan:如果您喜欢我的建议,请毫不犹豫地投票赞成;)除了不留下文件之外,它还具有允许多个进程同时运行此文件的优点,并且当前目录不需要可写。

– jlliagre
2011年6月8日20:40

#13 楼

以下“ if”块仅在“命令”成功执行后才会运行:

if command; then
   # ...
fi


具体地说,您可以运行以下内容:

haconf_out=/path/to/some/temporary/file

if haconf -makerw > "$haconf_out" 2>&1; then
   grep -iq "Cluster already writable" "$haconf_out"
   # ...
fi


将运行haconf -makerw并将其stdout和stderr存储到“ $ haconf_out”。如果从haconf返回的值为true,则将执行'if'块,并且grep将读取“ $ haconf_out”,以使其与“群集已可写”匹配。

请注意,该管道会自动进行管道传输清理自己;使用重定向时,您必须小心完成后删除“ $ haconf_out”。

不如pipefail优雅,但是如果此功能无法实现,则是一个合理的选择。

#14 楼

Alternate example for @lesmana solution, possibly simplified.
Provides logging to file if desired.
=====
$ cat z.sh
TEE="cat"
#TEE="tee z.log"
#TEE="tee -a z.log"

exec 8>&- 9>&-
{
  {
    {
      { #BEGIN - add code below this line and before #END
./zz.sh
echo ${?} 1>&8  # use exactly 1x prior to #END
      #END
      } 2>&1 | ${TEE} 1>&9
    } 8>&1
  } | exit $(read; printf "${REPLY}")
} 9>&1

exit ${?}
$ cat zz.sh
echo "my script code..."
exit 42
$ ./z.sh; echo "status=${?}"
my script code...
status=42
$


#15 楼

(至少使用bash)与set -e结合使用时,可以使用子shell显式模拟pipefail并在出现管道错误时退出程序将不会执行,并且脚本会退出并显示相应的错误代码。
(假设foo打印自己的错误,足以理解失败的原因)

#16 楼

编辑:此答案是错误的,但很有趣,所以我将其留作以后参考。



向命令添加!会反转返回代码。

http://tldp.org/LDP/abs/html/exit-status.html

# =========================================================== #
# Preceding a _pipe_ with ! inverts the exit status returned.
ls | bogus_command     # bash: bogus_command: command not found
echo $?                # 127

! ls | bogus_command   # bash: bogus_command: command not found
echo $?                # 0
# Note that the ! does not change the execution of the pipe.
# Only the exit status changes.
# =========================================================== #


评论


我认为这无关。在您的示例中,我想知道ls的退出代码,而不要反转bogus_command的退出代码

– Michael Mrozek
2011年6月2日22:13

我建议退回该答案。

–maxschlepzig
2011年6月3日,9:15

好吧,看来我是个白痴。我实际上已经在脚本中使用过它,然后才认为它可以满足OP的要求。好东西,我没用它做任何重要的事情

–法尔玛里
2011年6月8日17:14