16

Как перенаправить вывод в файл и на экран (stdout)

13

В bash, выполнение команды foo выводит любой результат этой команды на стандартный вывод (stdout).

Если вызвать команду foo > output, то вывод этой команды будет перенаправлен в указанный файл (в данном случае 'output').

Существует ли способ перенаправить вывод в файл и одновременно отобразить его на стандартном выводе?

5 ответ(ов)

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
0

В моем случае у меня был процесс на Java с выводом логов. Самое простое решение для отображения выходных логов и перенаправления их в файл (в данном случае имя файла - logfile) выглядело так:

my_java_process_run_script.sh |& tee logfile

Результат: процесс на Java выполнялся с отображением выходных логов и их записью в файл с именем logfile.

0

Использование команды tail -f output должно сработать.

0

Бонусный ответ, так как этот случай привел меня сюда:

В том случае, если вам нужно сделать это от имени другого пользователя

echo "некоторый вывод" | sudo -u some_user tee -a /some/path/some_file

Обратите внимание, что команда echo будет выполняться от вашего имени, а запись в файл произойдет от имени "some_user". То, что НЕ будет работать – это если вы запустите echo от имени "some_user" и попытаетесь перенаправить вывод с помощью >> some_file, потому что перенаправление файла произойдет от вашего имени.

Подсказка: команда tee также поддерживает добавление с флагом -a. Если вам нужно заменить строку в файле от имени другого пользователя, вы можете использовать sed от имени нужного пользователя.

0

Вы можете сделать это для всего вашего скрипта, добавив что-то вроде следующего в начале вашего скрипта:

#!/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.

Хотя это выглядит некрасиво, но это полезно для меня в определенных ситуациях.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь