仅在命令成功的情况下如何抑制输出?


22

我想通过抑制通常成功的辅助命令的输出来简化脚本的输出。

但是,-q在它们上偶尔使用时,对它们使用会隐藏输出,因此我无法理解该错误。此外,这些命令将其输出记录在上stderr

是否只有在命令成功的情况下才可以抑制的输出?

例如(但不限于)这样的东西:

mycommand | fingerscrossed

如果一切顺利,则fingerscrossed捕获输出并将其丢弃。否则,它将回显到标准或错误输出(无论如何)。

Answers:


36

moreutils' chronic命令就是这样做的:

chronic mycommand

将会吞并mycommand输出,除非失败,在这种情况下将显示输出。


1
谢谢。我认为大多数Unix操作系统默认未安装它吗?
Matthieu Napoli

1
可能不是,尽管它被广泛包装,所以应该易于安装。
史蒂芬·基特

1
Debian在软件包中moreutils。无论如何对我来说都是好:)
Tom Zych

6
请注意,它将整个输出存储在内存中。
斯特凡Chazelas

1
@StéphaneChazelas可能是实现类似这样的唯一方法,需要在命令运行时存储输出,以备不时之需。
Centimane

11
### do this bit once at the top of your script
divert=
exec 3<>"${divert:=$(mktmp)}" 4<>/dev/null
rm -- "$divert"; unset divert
### then do this bit as often as needed
command >&3 2>&3
cat <&3 >&"$(((RTN=$?)?2:4))"

那应该可以解决问题。它将每个缓冲区的输出缓冲command到一个已删除的临时文件中,然后/dev/null根据其返回状态是否为零,将其输出虹吸到stderr或stderr中。由于临时文件会提前删除,因此任何进程都无法读取它,而是当前shell及其文件描述符上的子进程(除非/proc/$pid/fd具有适当权限的偷偷摸摸的窥探),并且在清理过程中不需要清理。

也许是在Linux系统上更方便的解决方案:

divert(){
    "$@" >&3 2>&3 ||
    eval "cat <&3
          return $?"
}   3<<"" 3<>/dev/fd/3

...在大多数shell中,它的工作方式与其他类似,只是您可以这样称呼它:divert some simple-command with args。当心高输出的命令"$@",虽然对dashyash或者一些其他的壳里面做这里的文档与管道-我认为这是可能的那些壳来填充管道缓冲区(在对Linux版本各地128KB的默认值),因此僵局。这不应该是一个隐忧kshmkshbashzsh,或者Bourne shell的,虽然-所有这些基本上做同样的事情,我做了明确以上exec


9

通常在发生错误的情况下,命令会向其输出消息,stderr因此对于您来说,您可以压制任务stdout

mycommand > /dev/null


谢谢,但是正如我在问题中所说的,我的命令将所有输出记录在日志上stderr(因此它没有任何作用)。
Matthieu Napoli

4

使自己成为慢性病

my_chronic() {
  tmp=$(mktemp) || return # this will be the temp file w/ the output
  "$@"  > "$tmp" 2>&1 # this should run the command, respecting all arguments
  ret=$?
  [ "$ret" -eq 0 ] || cat "$tmp"  # if $? (the return of the last run command) is not zero, cat the temp file
  rm -f "$tmp"
  return "$ret" # return the exit status of the command
}

3

我在我的makefile文件中做了这样的事情:

if (mycommand) &> mycommand.log; then 
  echo success 
else 
  c=$?; 
  echo;echo -e "Bad result from previous command, see mycommand.log for more details";echo;
  command_to_run_on_fail
  (exit $c)
fi

适应您的情况,您可以执行以下操作:

if ! (mycommand) &> mycommand.log; then 
  c=$?; 
  cat mycommand.log
  rm mycommand.log
  (exit $c)
fi

因此,“ if”运行命令并将输出通过管道传输到mycommand.log。如果您需要捕获stdout vs stdout vs之类的内容,则可能需要将管道命令“&>”更改为“>”。如果命令失败,则捕获错误代码,打印出mycommand.log的内容,删除mycommand.log,最后返回原始错误代码。

如果没有(exit $ c),您将返回与“ rm”命令返回的内容匹配的退出代码。

最后,如果您需要一个衬板,则可以使用类似的方法。

mycommand &> mycommand.log || cat mycommand.log; rm mycommand.log

2
你真的包装你的命令/等。在(...)这样呢?因为它对您没有任何帮助,但会产生额外的子外壳。
Etan Reisner

@EtanReisner (exit $c)正在设置$?,否则您将无法执行此操作。if ! (mycommand) &>x如果命令使用例如time或会给出shell错误,则对于重定向有意义。
Michael Homer

@MichaelHomer-对于那些东西来说,还有冰壶{ ; }……尽管exit在那里有些棘手,但诚然。
mikeserv '16

在makefile代码段中,如果您尝试使用先前保存的代码退出,$?则可以使用,exit $c但是可以,但在其他情况下(exit $?)还是有价值的(尽管rret() { return $1; }我一般认为shell函数会更好)。正如mikeserv指出的那样,命令的子外壳仍然没有。
Etan Reisner

3

我刚刚在另一个问题上找到了更简单的答案:

output=`mycommand 2>&1` || echo $output

奇迹般有效!


注意:对于我的用例,这是一个简单得多的解决方案(避免在所有CI服务器上安装多余的东西),因此我选择将其标记为已接受。YMMV。
Matthieu Napoli

请注意,如果您在shell脚本中使用set -o xtrace,则所有输出将再次记录在日志中,作为记录分配output = ... :-)的详细信息的一部分。在这种情况下,最好使用慢性病。
Jan-Philip Gehrcke
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.