bash: перенаправить (и добавить) stdout и stderr в файл и терминал и получить надлежащий статус выхода - PullRequest
35 голосов
/ 10 марта 2010

Чтобы перенаправить (и добавить) stdout и stderr в файл, а также отобразить его на терминале, я делаю это:

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

Однако, есть ли другой способ сделать это так, чтобы я получил точное значение для статуса выхода?

То есть, если я тестирую $?, я хочу видеть состояние выхода command, а не состояние выхода tee.

Я знаю, что могу использовать ${PIPESTATUS[0]} здесь вместо $?, но я ищу другое решение, которое не включало бы проверку PIPESTATUS.

Ответы [ 4 ]

30 голосов
/ 10 марта 2010

Возможно, вы могли бы поместить значение выхода из PIPESTATUS в $?

command 2>&1 | tee -a file.txt ; ( exit ${PIPESTATUS} )
6 голосов
/ 10 марта 2010

Другая возможность, с некоторыми bash ароматами, состоит в том, чтобы включить опцию pipefail:

pipefail

Если установлено, возвращаемое значение конвейера значение последней (самой правой) Команда для выхода с ненулевым статус или ноль, если все команды в успешный выход из трубопровода. это опция отключена по умолчанию.

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

Сказано, что единственный способ достижения функциональности PIPESTATUS переносимым образом (например, так, чтобы он также работал с POSIX sh) немного запутан, то есть требуется временной файл передать статус выхода канала обратно в родительский процесс оболочки:

{ 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 голосов
/ 02 июня 2015

Использовать подстановку процесса:

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

тройник работает в подоболочке, так что? содержит состояние выхода команды .

3 голосов
/ 11 марта 2010

Существует загадочный способ POSIX:

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

Он установит переменную R в возвращаемое значение command1, а выход канала от command1 до command2, чей вывод перенаправляется на выход родительской оболочки.

...