我发现 SE 上已经提出并回答了许多关于 Bash 中使用重定向的问题执行,但似乎没有人回答我的问题。
我想要完成的是将所有输出重定向到标准错误在脚本中保存到临时文件并将其恢复到标准错误仅在脚本未成功终止的情况下。恢复内容的能力标准错误在未成功终止的情况下需要用于向调用者返回有关错误的信息。重定向标准错误会/dev/null
丢弃不相关的噪音和有用的信息。应尽可能避免显式临时文件,因为它们会增加许多其他问题(请参阅https://dev.to/philgibbs/avoiding-temporary-files-in-shell-scripts)。的内容标准输出应透明地返回给调用者。如果脚本未成功终止,调用者将忽略它,但脚本不需要关心这一点。
这样做的原因是该脚本是从另一个程序调用的,该程序考虑任何输出标准错误错误,而不是测试非零退出代码。在下面的例子中,开放式SSL回显进度指示器标准错误这并不表示错误,并且在成功完成命令后可以忽略。
下面是我的脚本的简化版本(请注意开放式SSL这里只是任意且可能更复杂的指令的占位符):
#!/bin/bash
set -euo pipefail
# Redirect stderr to temp fd 3
exec 3>&2
# In case of error, copy contents of fd 3 to stderr
trap 'cat <&3 >&2' ERR
# Upon exit, restore original stderr and close fd 3
trap 'exec 2>&3 3>&-' EXIT
# This nonetheless outputs the progress indicator to stderr
openssl genpkey -algorithm RSA
但我显然遗漏了一些东西,因为脚本不断打印出内容标准错误成功终止时,但现在失败时阻塞。我想我对如何重定向感到困惑执行工作。我错了什么?
答案1
chronic
moreutils 几乎可以满足您的需求:
chronic cmd
将丢弃 cmd 的 stdout 和 stderr ,除非cmd
失败或被杀死,在这种情况下,chronic
将 stdout 输出写入 stdout ,然后将 stderr 输出写入保存在内存中的 stderr 。
chronic
编写的perl
可以处理任意数据并且可以独立地从两个流中读取。另一方面,zsh 以外的 shell 无法在其变量中存储任意输出,因为它们会阻塞或 NUL 字符。此外,命令替换(即使在 中zsh
)也会删除尾随换行符。
在这里,如果您从一开始就删除它,您仍然可以使用临时文件并消除与其相关的大部分问题(例如,大多数 shell 对其此处文档所做的操作):
#! /bin/bash -
set -o nounset -o errexit -o pipefail
tmp=$(mktemp)
exec 3>&2 2> "$tmp" 4< "$tmp"
rm -f -- "$tmp"
trap '[ "$?" -eq 0 ] || cat <&4 >&3' EXIT
cmd1
cmd2
答案2
感谢上面评论中的所有人,他们帮助我指明了正确的方向。下面这似乎是我一直在寻找的答案,深受启发这是另一个 SE 答案:
#!/bin/bash
set -euo pipefail
shopt -s inherit_errexit
trap 'printf "%s\n" "$_ERR" >&2' ERR
{ _ERR=$({
# Arbitrary commands go here
openssl genpkey -algorithm RSA
} 2>&1 >&3 3>&-); } 3>&1
欢迎提出进一步改进的想法。
感谢@StéphaneChazelas 的建议。