我想在 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(导致主脚本失败)?这是我尝试过的:
- stderr 输出重定向到文件,检查文件内容。
希望避免创建临时文件。 将 stderr 重定向到子 shell stdin,例如:
mycommand 2> >(if grep .; then echo NOK; else echo OK; fi)
这似乎工作正常,但是,我无法控制这里的主外壳退出,即
exit 1
不会退出主程序。我也无法控制子 shell 外部的变量来传播其结果。我真的必须创建一个命名管道之类的吗?设置额外的文件描述符,例如这个答案。
对我来说看起来不太优雅,真的。
一些“要求”:
- 它不应该在标准输出上的常规输出上失败(也在那里输出)。
- 我想在标准输出上保留其他有用的输出。
- 我想保留当前在 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