在Bash Shell脚本中结合管道和退出状态

9

我有一个bash shell脚本命令,如果make成功(以零退出状态结束),则会导致date运行,反之亦然:

make && date

但现在我想处理它的输出,例如:

make | sed s/a/A/

如果我执行
make | sed s/a/A/ && date
date即使在make失败时也会运行。
如果我使用以下命令:
make && date | sed s/a/A/

sed处理date的输出,而不是make的。

你是否知道任何解决方案?谢谢!


P.S. 我已经尝试过以下方法:

(make | sed s/a/A/) && date

日期仍在运行,即使制作失败。
(make && (date > /dev/null)) | sed s/a/A/

日期(date)命令不在make成功时运行。

1
在最后一个情况下((make && (date > /dev/null)) | sed s/a/A/),当 make 成功时 date 确实 运行,但是你将它的输出发送到了黑洞中,因此它没有任何效果... - Jan Hudec
2
请查看PIPESTATUS - Fredrik Pihl
3个回答

10

如果你的shell支持进程替换(bash支持,posix shell不支持),那么

make > >(sed s/a/A/) && date

这应该可以解决问题,但是Bash在执行sed命令时不会等待(似乎zsh会等待,但我只尝试过它,没有查看文档),因此date的输出可能会出现在sed输出的最后一行之前。在纯posix shell中,您可以使用稍微复杂一些的结构。

((make && date >&3) | sed s/a/A/) 3>&1

在 sed 处理完所有内容之前,日期可能会再次运行,因此它的输出可能会再次出现在 sed 输出的最后一行之前。

如果你希望日期仅在 sed 处理完所有内容之后运行,你唯一的机会就是将 make 状态存储在某个地方。例如:

(make && touch make-succeeded) | sed s/a/A/
rm make-succeeded 2>/dev/null && date

利用不存在文件时rm(不加-f选项)会以非零状态退出并通过重定向来消除错误消息的事实。正如Fredrik所提到的,bash确实有一个它可以存储退出状态的地方,因此在bash中你可以这样做:

make | sed s/a/A/
[ 0 -eq $PIPESTATUS[0] ] && date

谢谢!希望我能给你一个额外的积分,因为你考虑到我想要日期在sed输出之后出现。 - Gnubie

8

make子shell中打开pipefail

(set -o pipefail; make 2>/dev/null | sed s/a/A/) && date

1

基本上就像这样;

my_make () {
  local rc
  make >tmp
  rc=$?
  sed s/a/A/ tmp
  rm tmp
  return $rc
}

临时文件显然应该以适当的方式处理;这只是为了集中精力解决手头的问题。如果您在文件描述符方面很聪明,可能完全可以避免使用临时文件,但今天是星期五,我需要更多的咖啡。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接