我的目标是调用命令,在变量中获取 stderr,但将 stdout (并且仅 stdout)保留在屏幕上。是的,这与大多数人的做法相反:)
目前,我拥有的最好的是:
#!/bin/bash
pull=$(sudo ./pull "${TAG}" 2>&1)
pull_code=$?
if [[ ! "${pull_code}" -eq 0 ]]; then
error "[${pull_code}] ${pull}"
exit "${E_PULL_FAILED}"
fi
echo "${pull}"
但这只能在成功的情况下以及命令完成后显示标准输出。我想要直播标准输出,这可能吗?
编辑
感谢@sebasth,并在以下人员的帮助下将 STDERR 和 STDOUT 重定向到不同的变量,无需临时文件,我这样写:
#!/bin/bash
{
sudo ./pull "${TAG}" 2> /dev/fd/3
pull_code=$?
if [[ ! "${pull_code}" -eq 0 ]]; then
echo "[${pull_code}] $(cat<&3)"
exit "${E_PULL_FAILED}"
fi
} 3<<EOF
EOF
我承认这并不是真的“美丽”,看起来很棘手,而且我真的不明白为什么需要heredoc......
这是实现这一目标的更好方法吗?
答案1
只需使用:
{ err=$(cmd 2>&1 >&3 3>&-); } 3>&1
要获得 stderr,cmd
同时保持其 stdout 不变(此处使用 fd 3 来带来3>&1
命令替换中的原始标准输出(用 复制)(>&3
在我们将 fd 2 重定向到由命令替换创建的管道后恢复2>&1
))。
答案2
您可以使用以下提供的答案斯蒂芬·查泽拉斯稍加修改:仅重定向 stderr 并且不使用命令替换来运行命令。
{
command 2> /dev/fd/3
res=$?
err=$(cat<&3)
} 3<<EOF
EOF
printf 'stderr: %s\n' "$err"
的输出command
通常在 stdout 中,而 stderr 在err
变量中。
答案3
只需将 stdout 和 stderr 交换为捕获 stderr 的命令即可。
pull=$(sudo ./pull "${TAG}" 3>&2 2>&1 1>&3)
然后将 stderr 重定向回 stdout:
{ pull=$(sudo ./pull "${TAG}" 3>&2 2>&1 1>&3; } 2>&1
解释:
与仅捕获命令的标准输出时的方式相同:
var=$(cmd)
stderr 的输出仍然转到命令行。您可以交换两个通道以仅捕获 stderr:
var=$(cmd 3>&2 2>&1 1>&3)
然后,将 stderr 重定向回 stdout(以显示输出(如果有))。
{ var=$(f 3>&2 2>&1 1>&3); } 2>&1
答案4
结合双方的答案如何输出到屏幕覆盖重定向和如何捕获bash关键字的stderr(例如时间)?,我得到
var=$( (cmd >/dev/tty) 2>&1)
如果您不使用内置函数或关键字,则可以简化为
var=$( cmd 2>&1 >/dev/tty )