将标准输出和标准错误重定向(并追加)到文件和终端,并获取正确的退出状态

36

为了将标准输出(stdout)和标准错误(stderr)重定向(并追加)到一个文件中,同时在终端上显示输出内容,我通常采用以下方法:

command 2>&1 | tee -a file.txt

然而,是否有另一种方法可以做到这一点,以便我得到一个准确的退出状态值?

也就是说,如果我测试 $?,我想看到的是command的退出状态,而不是tee的退出状态。

我知道我可以在这里使用${PIPESTATUS[0]}代替$?,但我正在寻找另一种解决方案,不需要检查PIPESTATUS


2
为什么你不想使用 PIPESTATUS - Johannes Schaub - litb
重复问题:https://dev59.com/Q3NA5IYBdhLWcg3wYMt-,https://dev59.com/1nM_5IYBdhLWcg3wzmgw - jpalecek
4个回答

30

你可以将 PIPESTATUS 的退出值放入 $? 中。

command 2>&1 | tee -a file.txt ; ( exit ${PIPESTATUS} )

6

另一种可能性是在某些版本中打开pipefail选项:

pipefail

如果设置,管道的返回值为最后(最右边)一个以非零状态退出的命令的值,如果管道中所有命令都成功退出,则返回0。默认情况下未启用此选项。

set -o pipefail
...
command 2>&1 | tee -a file.txt || echo "Command (or tee?) failed with status $?"

话虽如此,实现可移植的PIPESTATUS功能(例如,它也可以与POSIX sh一起使用)的唯一方法有些复杂,即需要一个临时文件来将管道退出状态传播回父shell进程:

{ command 2>&1 ; echo $? >"/tmp/~pipestatus.$$" ; } | tee -a file.txt
if [ "`cat \"/tmp/~pipestatus.$$\"`" -ne 0 ] ; then
  ...
fi

或者,为了重用而封装:

log2file() {
  LOGFILE="$1" ; shift
  { "$@" 2>&1 ; echo $? >"/tmp/~pipestatus.$$" ; } | tee -a "$LOGFILE"
  MYPIPESTATUS="`cat \"/tmp/~pipestatus.$$\"`"
  rm -f "/tmp/~pipestatus.$$"
  return $MYPIPESTATUS
}

log2file file.txt command param1 "param 2" || echo "Command failed with status $?"

或者,更普遍地说:
save_pipe_status() {
  STATUS_ID="$1" ; shift
  "$@"
  echo $? >"/tmp/~pipestatus.$$.$STATUS_ID"
}

get_pipe_status() {
  STATUS_ID="$1" ; shift
  return `cat "/tmp/~pipestatus.$$.$STATUS_ID"`
}

save_pipe_status my_command_id ./command param1 "param 2" | tee -a file.txt
get_pipe_status my_command_id || echo "Command failed with status $?"

...

rm -f "/tmp/~pipestatus.$$."* # do this in a trap handler, too, to be really clean

4

使用进程替换:

command > >( tee -a "$logfile" ) 2>&1

tee在子shell中运行,因此$?保存命令的退出状态。


4

有一种晦涩的 POSIX 方法可以实现这个:

exec 4>&1; R=$({ { command1; echo $? >&3 ; } | { command2 >&4; } } 3>&1); exec 4>&-

它将把变量R设置为command1的返回值,并将command1的输出导入到command2,其输出被重定向到父Shell的输出。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接