Как перенаправить вывод в файл и на экран (stdout)
В bash, выполнение команды foo
выводит любой результат этой команды на стандартный вывод (stdout).
Если вызвать команду foo > output
, то вывод этой команды будет перенаправлен в указанный файл (в данном случае 'output').
Существует ли способ перенаправить вывод в файл и одновременно отобразить его на стандартном выводе?
5 ответ(ов)
$ программа [аргументы...] 2>&1 | tee outfile
2>&1
объединяет потоки stderr и stdout.
tee outfile
принимает поток, который он получает, и записывает его как на экран, так и в файл "outfile".
Скорее всего, именно это большинство людей и ищет. Вероятная ситуация такова: какая-то программа или скрипт выполняется долго и генерирует много вывода. Пользователь хочет периодически проверять прогресс, но также хочет, чтобы вывод записывался в файл.
Проблема (особенно при смешивании потоков stdout и stderr) заключается в том, что есть зависимость от того, как программа выполняет сброс потоков. Если, например, все записи в stdout не сбрасываются, но все записи в stderr сбрасываются, то они окажутся в выводе и на экране не в хронологическом порядке.
Также плохо, если программа выводит всего 1 или 2 строки каждые несколько минут для отчета о прогрессе. В таком случае, если вывод не был сброшен программой, пользователь может вообще не увидеть никаких данных на экране в течение часов, поскольку ничего не будет продвигаться через конвейер.
Обновление: Программа unbuffer
, входящая в пакет expect
, решит проблему буферизации. Это заставит stdout и stderr записываться на экран и в файл сразу же и поддерживать их синхронность при объединении и перенаправлении в tee
. Например:
$ unbuffer программа [аргументы...] 2>&1 | tee outfile
В моем случае у меня был процесс на Java с выводом логов. Самое простое решение для отображения выходных логов и перенаправления их в файл (в данном случае имя файла - logfile) выглядело так:
my_java_process_run_script.sh |& tee logfile
Результат: процесс на Java выполнялся с отображением выходных логов и их записью в файл с именем logfile.
Использование команды tail -f output
должно сработать.
Бонусный ответ, так как этот случай привел меня сюда:
В том случае, если вам нужно сделать это от имени другого пользователя
echo "некоторый вывод" | sudo -u some_user tee -a /some/path/some_file
Обратите внимание, что команда echo
будет выполняться от вашего имени, а запись в файл произойдет от имени "some_user". То, что НЕ будет работать – это если вы запустите echo
от имени "some_user" и попытаетесь перенаправить вывод с помощью >> some_file
, потому что перенаправление файла произойдет от вашего имени.
Подсказка: команда tee
также поддерживает добавление с флагом -a
. Если вам нужно заменить строку в файле от имени другого пользователя, вы можете использовать sed
от имени нужного пользователя.
Вы можете сделать это для всего вашего скрипта, добавив что-то вроде следующего в начале вашего скрипта:
#!/usr/bin/env bash
test x$1 = x$'\x00' && shift || { set -o pipefail ; ( exec 2>&1 ; $0 $'\x00' "$@" ) | tee mylogfile ; exit $? ; }
# Делайте все, что вам нужно
Этот код перенаправляет как stderr, так и stdout в файл с именем mylogfile
, и одновременно выводит все на stdout.
В этом используется несколько хитрых трюков:
exec
без команды для установки перенаправлений,tee
для дублирования выводов,- перезапуск скрипта с необходимыми перенаправлениями,
- использование специального первого параметра (простой символ
NUL
, заданный с помощью специальной нотации$'string'
в bash), чтобы указать, что скрипт перезапускается (исходный скрипт не может использовать эквивалентный параметр), - попытка сохранить исходный статус выхода при перезапуске скрипта с помощью опции
pipefail
.
Хотя это выглядит некрасиво, но это полезно для меня в определенных ситуациях.
Как изменить цвет вывода echo в Linux
Как перенаправить stderr, а не stdout?
Как работает "cat << EOF" в bash?
Как прочитать большой текстовый файл построчно с помощью Java?
Как использовать 'grep' для непрерывного потока?