Подтвердить что ты не робот

Bash: перенаправить (и добавить) stdout и stderr в файл и терминал и получить правильный статус выхода

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

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

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

Другая возможность, с некоторыми 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
  { "[email protected]" 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
  "[email protected]"
  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

Ответ 3

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

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

tee работает в подоболочке, поэтому $? содержит статус выхода команды.

Ответ 4

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

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

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