Я знаю, как перенаправить стандартный вывод в файл:
exec > foo.log
echo test
это поместит 'test' в файл foo.log.
Теперь я хочу перенаправить вывод в файл журнала и сохранить его на стандартный вывод
то есть это можно сделать тривиально снаружи скрипта:
script | tee foo.log
но я хочу сделать объявление в самом скрипте
Я попытался
exec | tee foo.log
но это не сработало.
Ответы:
Обратите внимание, что это
bash
не такsh
. Если вы вызовете скрипт с помощьюsh myscript.sh
, вы получите сообщение об ошибкеsyntax error near unexpected token '>'
.Если вы работаете с ловушками сигнала, вы можете использовать эту
tee -i
опцию, чтобы избежать прерывания выхода при возникновении сигнала. (Спасибо JamesThomasMoon1979 за комментарий.)Инструменты, которые изменяют свои выходные данные в зависимости от того, пишут ли они в канал или терминал (например,
ls
используют цвета и столбчатый вывод), обнаружат вышеуказанную конструкцию как означающую, что они выводят в канал.Есть варианты для принудительного раскрашивания / колонизации (например
ls -C --color=always
). Обратите внимание, что это приведет к записи цветовых кодов в файл журнала, что сделает его менее читаемым.источник
tee
не должен буферизовать свой вывод. Если это делает буфер на большинстве систем, это сломано на большинстве систем. Это проблемаtee
реализаций, а не моего решения.exec
очень мощный, но и очень вовлеченный. Вы можете «создать резервную копию» текущего стандартного вывода в другом дескрипторе файла, а затем восстановить его. Google "bash exec tutorial", есть много продвинутых вещей.exec
это документально не начинать новые процессы,>(tee ...)
является стандартом по имени подмена труб / процесс, и&
в перенаправлении , конечно , не имеет ничего общего с ... :-) фоновый?-i
кtee
. В противном случае прерывания сигнала (traps) нарушат стандартный вывод основного сценария. Например, если у вас есть,trap 'echo foo' EXIT
а затем нажмитеctrl+c
, вы не увидите « foo ». Поэтому я бы изменил ответ наexec &> >(tee -ia file)
.Принятый ответ не сохраняет STDERR как отдельный дескриптор файла. Это значит
не будет выводиться
bar
на терминал, только в лог-файл, ибудет выводить как
foo
иbar
на терминал. Понятно, что такое поведение не может ожидать обычный пользователь. Это можно исправить с помощью двух отдельных процессов, которые добавляются в один и тот же файл журнала:(Обратите внимание, что вышеупомянутое изначально не усекает файл журнала - если вы хотите такое поведение, вы должны добавить
в начало сценария.)
Спецификация POSIX.1-2008
tee(1)
требует, чтобы вывод был небуферизованным, то есть даже не буферизованным строкой, поэтому в этом случае возможно, что STDOUT и STDERR могут оказаться в одной строкеfoo.log
; однако это также может произойти на терминале, поэтому файл журнала будет точным отражением того, что можно увидеть на терминале, если не будет точным отражением этого. Если вы хотите, чтобы строки STDOUT были четко отделены от строк STDERR, рассмотрите возможность использования двух файлов журнала, возможно, с префиксами отметок даты в каждой строке, чтобы впоследствии разрешить их повторную сборку в хронологическом порядке.источник
exec > >(tee -a $LOG)
trap "kill -9 $! 2>/dev/null" EXIT
exec 2> >(tee -a $LOG >&2)
trap "kill -9 $! 2>/dev/null" EXIT
-i
кtee
. В противном случае прерывания сигнала (прерывания) нарушат стандартный вывод в сценарии. Например, если выtrap 'echo foo' EXIT
и затемctrl+c
нажмете, вы не увидите « foo ». Поэтому я бы изменил ответ наexec > >(tee -ia foo.log)
.. log
или. log foo.log
: sam.nipl.net/sh/log sam.nipl.net/sh/log-aSTDOUT
появляться сначала в виде пакета, а затем будутSTDERR
появляться сообщения. Они не чередуются, как обычно ожидалось.Решение для busybox, macOS bash и не-bash оболочек
Принятый ответ, безусловно, лучший выбор для bash. Я работаю в среде Busybox без доступа к bash, и он не понимает
exec > >(tee log.txt)
синтаксис. Он также не работаетexec >$PIPE
должным образом, пытаясь создать обычный файл с тем же именем, что и именованный канал, который не работает и зависает.Надеюсь, это будет полезно для кого-то еще, у кого нет bash.
Кроме того, для любого, кто использует именованный канал, это безопасно
rm $PIPE
, потому что это освобождает канал от VFS, но процессы, которые его используют, все еще поддерживают счетчик ссылок на него, пока не будут завершены.Обратите внимание, что использование $ * не обязательно безопасно.
источник
Внутри вашего скрипта поместите все команды в круглые скобки, например так:
источник
{}
)Простой способ сделать журнал сценария bash в системном журнале. Вывод скрипта доступен как через, так
/var/log/syslog
и через stderr. Системный журнал добавит полезные метаданные, включая метки времени.Добавьте эту строку вверху:
В качестве альтернативы, отправьте журнал в отдельный файл:
Это требует
moreutils
(дляts
команды, которая добавляет метки времени).источник
Используя принятый ответ, мой скрипт возвращался исключительно рано (сразу после 'exec>> (tee ...)'), оставляя остальную часть моего скрипта работающей в фоновом режиме. Поскольку я не мог заставить это решение работать по-своему, я нашел другое решение / решение этой проблемы:
Это приводит к тому, что выходные данные из скрипта переходят из процесса через канал в суб-фоновый процесс 'tee', который записывает все на диск и в исходный стандартный вывод скрипта.
Обратите внимание, что 'exec &>' перенаправляет как stdout, так и stderr, мы можем перенаправить их отдельно, если захотим, или изменить на 'exec>', если нам просто нужен stdout.
Даже если канал удален из файловой системы в начале сценария, он продолжит функционировать до завершения процессов. Мы просто не можем ссылаться на него, используя имя файла после строки rm.
источник
$logfile
частьtee < ${logfile}.pipe $logfile &
. В частности, я попытался изменить это, чтобы захватить полностью развернутые строки журнала команд (изset -x
) в файл, показывая только строки без начального «+» в stdout, изменив на,(tee | grep -v '^+.*$') < ${logfile}.pipe $logfile &
но получив сообщение об ошибке, касающееся$logfile
. Можете ли вы объяснитьtee
строку более подробно?Bash 4 имеет
coproc
команду, которая устанавливает именованный канал для команды и позволяет вам общаться через нее.источник
Не могу сказать, что мне удобно какое-либо из решений, основанных на exec. Я предпочитаю использовать tee напрямую, поэтому я вызываю сам скрипт с tee по запросу:
Это позволяет вам сделать это:
Вы можете настроить это, например, сделать вместо этого значение по умолчанию: tee = false, вместо этого сделать так, чтобы TEE удерживал файл журнала, и т. Д. Я думаю, что это решение похоже на jbarlow, но проще, возможно, у меня есть ограничения, с которыми я еще не сталкивался.
источник
Ни один из них не является идеальным решением, но вот несколько вещей, которые вы можете попробовать:
или
Второй оставил бы трубу без дела, если что-то пойдет не так с вашим сценарием, что может быть или не быть проблемой (то есть, возможно, вы могли бы
rm
потом это сделать в родительской оболочке).источник
tee
- я редактировал. Как я уже сказал, ни одно из них не является идеальным решением, но фоновые процессы будут убиты, когда завершится их родительская оболочка, так что вам не придется беспокоиться о том, что они будут затягивать ресурсы вечно.