Перенаправити всі наступні команди '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

але це не працює. Хтось має рішення / може пояснити причину?


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.


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

2
Зауважте, що тут використовуються башизми - він може не працювати в інших оболонках (наприклад, тире). Але оскільки в питанні вказано баш, +1.
Річард Хансен

8
@RichardHansen, заміна процесу - це особливість, яку запровадив ksh, а не bash, а також підтримується zsh, тому я б не назвав це башизмом .
Стефан Шазелас

6
@StephaneChazelas: Ви добре зазначаєте. Я просто хотів зазначити, що синтаксис не підтримується стандартом POSIX і, отже, не буде універсально працювати в /bin/shсценаріях (багато людей помилково використовують синтаксис bash у /bin/shскриптах).
Річард Хансен

Для мене це дає /dev/fd/62: Operation not supportedякісь підказки?
Євн

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"

Корисно для кронів, тому ви можете отримувати помилки (і лише помилки) поштою

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.