Скопируйте stdout и stderr в файл журнала и оставьте их на консоли в самом скрипте

11

Используя bash, как мне скопировать stderr и stdout в файл журнала, а также оставить их отображенными на консоли?

Я хотел бы сделать это в самом скрипте, используя exec.

Я пробовал с

exec &>> log.out

echo "This is stdout"
echo "This is stderr" >&2

Но вышесказанное ничего не печатает на консоли. Как я могу добиться этого в Bash?

adarshr
источник
На StackOverflow есть ответ с большим количеством голосов, который достаточно подробно отвечает на этот вопрос. stackoverflow.com/a/692407/208257
Дэн Бертон

Ответы:

10

Вы ищете tee.

Смотрите man teeподробности.

Чтобы объединить это exec, вы должны использовать процесс подстановки . (Подробнее man bashсм.)

exec &> >(tee  log.out)
echo "This is stdout"
echo "This is stderr" >&2
Х.-Дирк Шмитт
источник
Я посмотрел на это. Делаем exec 2>&1 | tee -a log.outтолько распечатки на консоли, ничего в лог-файле. Может быть, я не использую правильный синтаксис?
adarshr
@adarshr Пожалуйста, проверьте ваш код еще раз. Я написал teeв сочетании с процессом замены .
Х.-Дирк Шмитт
1
Это объединяет stdout и stderr и может иметь такой же побочный эффект, как упомянуто в моем ответе.
Стефан Шазелас
@StephaneChazelas Посмотрите на пример кода в вопросе - это было намерение от ОП.
Х.-Дирк Шмитт
Под слиянием я подразумеваю, что теперь ошибки скрипта переходят в стандартный вывод. Так что, если кто-то, the-script | wc -lнапример, сделает это, он также посчитает строки ошибок, и вы не увидите ошибок.
Стефан Шазелас
5

Ты можешь сделать:

: > log # empty log file if necessary
{ { {

  ...the script

} 3>&- | tee -a log >&3 3>&-
exit "${PIPESTATUS[0]}"
} 2>&1 | tee -a log >&2 3>&-
} 3>&1
exit "${PIPESTATUS[0]}"

Вы также можете написать это как:

: > log # empty log file if necessary
exec 2> >(tee -a log >&2) > >(tee -a log)

...the script

Но поскольку bash не ожидает запуска этих процессов >(...), это имеет неприятный эффект: иногда выводить что-то на терминал после возврата команды, что может иметь еще более неприятные последствия (например, молча отбрасывать этот вывод), если атрибут терминала «tostop» включен

В любом случае, создавая stdoutканал в обоих решениях и поскольку две команды независимо выводят выходные данные и сообщения об ошибках, это повлияет на буферизацию вывода и порядок отображения выходных сообщений и сообщений об ошибках.

Стефан Шазелас
источник
Я думаю, что ваш второй тройник пропускает редирект на stderr. Это должно быть:tee -a log >&2 3>&-
richvdh
5

Я знаю, что это старый пост, но почему бы просто не сделать это?

echo "hi" >> log.txt #stdout -> log
echo "hi" | tee -a log.txt #stdout -> log & stdout
echo "hi" &>> log.txt #stdout & stderr -> log
echo "hi" |& tee -a log.txt #stdout & stderr -> log & stdout

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

Вы можете сделать это с любой комбинацией потоков, которую вы пожелаете, просто используя эти две основные команды.

Я знаю, что пришел сюда и не получил легкого для понимания / реализации ответа, надеюсь, это поможет кому-то еще, кто борется.

Между прочим, для noobs, как и мой предыдущий self, все, что teeделает команда, это выводит ввод stdin как в stdout, так и в файл (ы), указанные в качестве последующих аргументов. -aозначает добавление, поэтому вы не перезаписываете файл при каждом использовании команды. Если у вас есть дополнительные вопросы, я считаю , это будет очень полезным ресурсом для быстрого обучения Баша.

user2624583
источник
1
Спасибо за помощь. Я, с другой стороны, совершенно забыл, что я пытался сделать :-)
adarshr
1
+1 за множественные, красивые и простые примеры.
Тим
2

Еще один способ сделать это - использовать перенаправления внутри функций.

#!/bin/bash

function1 () {
    echo 'STDOUT from function 1'
    echo 'STDERR from function 1' >&2
}

function2 () {
    echo 'STDOUT from function 2'
    echo 'STDERR from function 2' >&2
}


function3 () {
    echo 'STDOUT from function 3'
    echo 'STDERR from function 3' >&2
}

main() {
    function1
    function2
    function3
}

main 2>&1 |tee log.txt

Здесь у нас есть mainфункция, которая вызывает все другие функции. Теперь перенаправлять STDOUTи STDERRиз mainфункции tee.

Каннан Мохан
источник
ИМХО, это самый чистый способ сделать это, по крайней мере, для простых случаев. Спасибо.
ACK_stoverflow