如何在变量中重定向 stderr 但将 stdout 保留在控制台中

如何在变量中重定向 stderr 但将 stdout 保留在控制台中

我的目标是调用命令,在变量中获取 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 )

相关内容