Перенаправить stderr всех последующих команд, используя exec

43

У меня есть файл bash, который мне нужен, чтобы перенаправить весь вывод в один файл, журнал отладки, а также в терминал. Мне нужно перенаправить как stdout, так и stderr к отладке и зарегистрировать его для всех команд в сценарии.

Я не хочу добавлять 2>&1 | tee -a $DEBUGдля каждой команды в файле. Я мог бы жить с | tee -a $DEBUG.

Я помню, что был способ сделать это с чем-то вроде exec 2>&1.

В настоящее время я использую что-то вроде следующего:

#!/bin/bash
DEBUGLOG=/tmp/debug
exec 2>&1
somecommand | tee -a $DEBUGLOG
somecommand2 | tee -a $DEBUGLOG
somecommand3 | tee -a $DEBUGLOG

Но это не работает. У кого-нибудь есть решение / может объяснить причину?

Avi
источник
1
В некоторых оболочках |&работает как ярлык 2>&1 |, хотя бы немного удобнее.
Кевин

Ответы:

39

Что касается решения для перенаправления большого количества команд одновременно:

#!/bin/bash
{
    somecommand 
    somecommand2
    somecommand3
} 2>&1 | tee -a $DEBUGLOG

Почему ваше оригинальное решение не работает: exec 2> & 1 перенаправит стандартный вывод ошибок на стандартный вывод вашей оболочки, который, если вы запустите скрипт из консоли, станет вашей консолью. перенаправление канала на команды будет перенаправлять только стандартный вывод команды.

С точки зрения somecommand, его стандартный вывод идет в канал, связанный с, teeи стандартная ошибка идет в тот же файл / псевдофайл, что и стандартная ошибка оболочки, которую вы перенаправляете на стандартный вывод оболочки, который будет консоль, если вы запускаете свою программу из консоли.

Единственный верный способ объяснить это - посмотреть, что на самом деле происходит:

Исходная среда вашей оболочки может выглядеть так, если вы запустите ее из терминала:

stdin -> /dev/pts/42
stdout -> /dev/pts/42
stderr -> /dev/pts/42

После того, как вы перенаправили стандартную ошибку в стандартный вывод ( exec 2>&1), вы ... ничего не меняете. Но если вы перенаправите стандартный вывод скрипта в файл, вы получите такую ​​среду:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /dev/pts/42

Тогда перенаправление стандартной ошибки оболочки в стандартный вывод закончится так:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /your/file

Выполнение команды унаследует эту среду. Если вы запустите команду и передадите ее по каналу, среда команды будет такой:

stdin -> /dev/pts/42
stdout -> pipe:[4242]
stderr -> /your/file

Таким образом, стандартная ошибка вашей команды все еще входит в то, что оболочка использует в качестве стандартной ошибки.

Вы можете фактически увидеть среду команды, посмотрев в /proc/[pid]/fd: используйте, ls -lчтобы также перечислить содержимое символической ссылки. 0Файл здесь стандартный ввод, 1стандартный выход и 2стандартная ошибка. Если команда открывает больше файлов (и большинство программ открывают), вы также увидите их. Программа также может выбрать перенаправление или закрытие стандартного ввода / вывода и повторное использование 0, 1и 2.

BatchyX
источник
41

Вы можете использовать exec вот так вверху вашего скрипта:

exec > >(tee "$HOME/somefile.log") 2>&1

Например:

#!/bin/bash -

exec > >(tee "$HOME/somefile.log") 2>&1

echo "$HOME"
echo hi
date
date +%F
echo bye 1>&2

Дает мне вывод в файл $HOME/somefile.logи на терминал, как это:

/home/saml
hi
Sun Jan 20 13:54:17 EST 2013
2013-01-20
bye
SLM
источник
2
Обратите внимание, что здесь используются bashisms - он может не работать в других оболочках (например, dash). Но поскольку вопрос задан bash, +1.
Ричард Хансен
8
@RichardHansen, подстановка процессов - это функция, которая была введена ksh, а не bash, а также поддерживается zsh, поэтому я бы не назвал это bashism .
Стефан Шазелас
6
@ StephaneChazelas: Вы делаете хорошую мысль. Я просто хотел отметить, что синтаксис не поддерживается стандартом POSIX и, следовательно, не будет универсально работать в /bin/shсценариях (многие люди ошибочно используют синтаксис bash в /bin/shсценариях).
Ричард Хансен
Для меня это дает /dev/fd/62: Operation not supportedкакие-либо подсказки?
Eun
1
Есть ли способ не перенаправлять stderr, кроме как в файл журнала? Если исходный сценарий есть, myscriptи я запускаю ./myscript > /dev/null, я все равно должен увидеть, byeоткуда echo bye >&2.
Мартин Джамбон
0

Записать stderr и stdout в файл, отобразить stderr на экране (на stdout)

exec 2> >(tee -a -i "$HOME/somefile.log")
exec >> "$HOME/somefile.log"

Полезно для крон, поэтому вы можете получать ошибки (и только ошибки) по почте

Lluís
источник