对于 shell/Bash 中 stderr 上打印的任何内容是否有一个简单的测试?

对于 shell/Bash 中 stderr 上打印的任何内容是否有一个简单的测试?

我想在 shell 脚本中调用命令以实现持续集成。退出状态0表示成功,否则失败。我正在编写一个包装器脚本来运行多个命令,如果其中一个命令发生任何错误,则会失败。

但是,其中一个命令(第 3 方软件)不符合失败时的“事实上的”退出状态 != 1。但是,如果出现故障,它会在 stderr 上打印错误。

当前的包装器脚本,如果两者都可以正常工作,mycommand并且other-command由于-e切换而会失败并退出状态!= 0:

#!/bin/bash -ex
mycommand --some-argument /some/path/to/file
other-command --other-argument /some/other/file

如何检查是否有任何内容打印到 stderr(导致主脚本失败)?这是我尝试过的:

  1. stderr 输出重定向到文件,检查文件内容。
    希望避免创建临时文件。
  2. 将 stderr 重定向到子 shell stdin,例如:

    mycommand 2> >(if grep .; then echo NOK; else echo OK; fi)
    

    这似乎工作正常,但是,我无法控制这里的主外壳退出,即exit 1不会退出主程序。我也无法控制子 shell 外部的变量来传播其结果。我真的必须创建一个命名管道之类的吗?

  3. 设置额外的文件描述符,例如这个答案
    对我来说看起来不太优雅,真的。

一些“要求”:

  • 它不应该在标准输出上的常规输出上失败(也在那里输出)。
  • 我想在标准输出上保留其他有用的输出。
  • 我想保留当前在 stderr 上打印的任何输出(可以是 stdout,但不应隐藏)。

因此它的行为应该像一个包装器,仅以不干净的状态退出,保留打印输出。

我只是希望有更优雅的东西来检查 stderr 中的任何内容。侮辱是可以接受的。

答案1

你可以这样做(POSIXly):

if { cmd 2>&1 >&3 3>&- | grep '^' >&2; } 3>&1; then
  echo there was some output on stderr
fi

或者保留原始退出状态(如果它非零):

fail_if_stderr() (
  rc=$({
    ("$@" 2>&1 >&3 3>&- 4>&-; echo "$?" >&4) |
    grep '^' >&2 3>&- 4>&-
  } 4>&1)
  err=$?
  [ "$rc" -eq 0 ] || exit "$rc"
  [ "$err" -ne 0 ] || exit 125
) 3>&1

125对于命令返回 0 退出状态但产生一些错误输出的情况,使用退出代码。

用作:

fail_if_stderr cmd its args || echo "Failed with $?"

答案2

# output "NOK" if standard error has any output; "OK" otherwise:
errlog=$(mktemp)
somecommand 1>> "$stdlog" 2> "$errlog"
if [[ -s "$errlog" ]]; then
    # File exists and has a size greater than zero
    echo "NOK"
else
    echo "OK"  
fi
# Done parsing standard error; tack it to the regular log
cat "$errlog" >> "$stdlog"
rm -f "$errlog"

答案3

得票最多的答案在大多数情况下都有效。但由于我set +o errexit在 bash 中使用它,所以出错了。这应该在 bash 中工作得更好:

fail_if_stderr() (
  # save current options
  bash_options="${-}"
  
  # disable exit on error
  set +o errexit
  
  # Save return code of command in rc
  rc=$({
    ("$@" 2>&1 >&3 3>&- 4>&-; echo "$?" >&4) |
    grep '^' >&2 3>&- 4>&-
  } 4>&1)
  
  # Save return code of grep in err_in_stderr
  err_in_stderr=$?
  
  # enable exit on error if it was previously enabled
  test "${bash_options#*e*}" != "$bash_options" && set -o errexit
  
  # exit with original return code if it's not zero
  [ "$rc" -eq 0 ] || exit "$rc"
  
  # exit with return code 125 if something was in stderr
  [ "$err_in_stderr" -ne 0 ] || exit 125
) 3>&1

相关内容