У меня есть сценарий, который будут запускать в интерактивном режиме нетехнические пользователи. Сценарий записывает обновления статуса в STDOUT, чтобы пользователь мог быть уверен, что сценарий работает нормально.
Я хочу, чтобы и STDOUT, и STDERR были перенаправлены на терминал (чтобы пользователь мог видеть, что сценарий работает, а также видеть, возникла ли проблема). Я также хочу, чтобы оба потока были перенаправлены в файл журнала.
Я видел кучу решений в сети. Некоторые не работают, а другие ужасно сложны. Я разработал работоспособное решение (которое я введу в качестве ответа), но оно неуклюже.
Идеальным решением была бы одна строка кода, которую можно было бы включить в начало любого скрипта, который отправляет оба потока как на терминал, так и в файл журнала.
РЕДАКТИРОВАТЬ: перенаправление STDERR в STDOUT и передача результата в tee работает, но это зависит от пользователей, которые не забывают перенаправить и передать вывод. Я хочу, чтобы ведение журнала было надежным и автоматическим (поэтому я хотел бы иметь возможность встроить решение в сам скрипт).
Ответы:
Используйте «тройник» для перенаправления в файл и на экран. В зависимости от используемой оболочки вам сначала нужно перенаправить stderr на stdout, используя
или
В csh есть встроенная команда под названием «скрипт», которая фиксирует все, что попадает на экран, в файл. Вы запускаете его, набирая «script», затем выполняя все, что хотите захватить, затем нажимаете Ctrl-D, чтобы закрыть файл сценария. Я не знаю эквивалента для sh / bash / ksh.
Кроме того, поскольку вы указали, что это ваши собственные сценарии sh, которые вы можете изменять, вы можете выполнить внутреннее перенаправление, заключив весь сценарий в фигурные скобки или скобки, например
источник
2>&1 | tee -a filename
не сохранял stderr в файл из моего скрипта, но он работал нормально, когда я скопировал команду и вставил ее в терминал! Однако трюк с скобками работает нормально.Приближается полдесятилетия спустя ...
Я считаю, что это «идеальное решение», к которому стремится ОП.
Вот один лайнер, который вы можете добавить в начало вашего сценария Bash:
Вот небольшой скрипт, демонстрирующий его использование:
(Примечание: это работает только с Bash. Это не будет работать с / bin / sh.)
Адаптировано отсюда ; оригинал, насколько я могу судить, не улавливал STDERR в файле журнала. Исправлено примечанием отсюда .
источник
Шаблон
Это перенаправляет как stdout, так и stderr отдельно, и отправляет отдельные копии stdout и stderr вызывающей стороне (которая может быть вашим терминалом).
В zsh он не перейдет к следующему оператору, пока
tee
s не завершится.В bash вы можете обнаружить, что последние несколько строк вывода появляются после любого следующего оператора.
В любом случае правильные биты попадают в нужные места.
Объяснение
Вот сценарий (хранится в ./example):
Вот сеанс:
Вот как это работает:
tee
процесса запускаются, их stdins назначаются файловым дескрипторам. Поскольку они заключены в подстановки процессов , пути к этим файловым дескрипторам подставляются в вызывающую команду, поэтому теперь это выглядит примерно так:the_cmd 1> /proc/self/fd/13 2> /proc/self/fd/14
the_cmd
запускается, записывая stdout в первый файловый дескриптор и stderr во второй.В случае bash, после
the_cmd
завершения, немедленно выполняется следующий оператор (если ваш терминал является вызывающим, вы увидите, что появляется ваше приглашение).В случае zsh после
the_cmd
завершения оболочка ожидает завершения обоихtee
процессов, прежде чем продолжить . Подробнее об этом здесь .Первый
tee
процесс, который читает изthe_cmd
stdout, записывает копию этого stdout обратно вызывающей стороне, потому что это то, чтоtee
делает. Его выходы не перенаправляются, поэтому они возвращаются к вызывающему без изменений.Второй
tee
процессstdout
перенаправляет его вызывающемуstderr
(что хорошо, потому что stdin читает изthe_cmd
stderr). Поэтому, когда он записывает в свой стандартный вывод, эти биты переходят в стандартный поток вызывающего.Это сохраняет stderr отдельно от stdout как в файлах, так и в выводе команды.
Если первый tee записывает какие-либо ошибки, они будут отображаться как в файле stderr, так и в stderr команды, если второй tee записывает какие-либо ошибки, они будут отображаться только в stderr терминала.
источник
tee
доступен в рассматриваемой системе.) Я получаю сообщение об ошибке: «Процесс не может получить доступ к файлу, потому что он используется другим процессом».для перенаправления stderr на stdout добавьте это в своей команде:
2>&1
Для вывода на терминал и входа в файл вы должны использоватьtee
Оба вместе будут выглядеть так:
РЕДАКТИРОВАТЬ: для встраивания в свой скрипт вы сделаете то же самое. Итак, ваш сценарий
закончится как
источник
РЕДАКТИРОВАТЬ: Я вижу, что я сошел с рельсов и в конечном итоге ответил на вопрос, отличный от заданного. Ответ на настоящий вопрос находится в конце ответа Пола Томблина. (Если вы по какой-то причине хотите улучшить это решение для перенаправления stdout и stderr отдельно, вы можете использовать метод, который я описываю здесь.)
Я давно хотел получить ответ, который сохранит различие между stdout и stderr. К сожалению, все ответы, данные до сих пор, которые сохраняют это различие, предрасположены к расе: они рискуют увидеть в программах неполный ввод, как я отмечал в комментариях.
Думаю, я наконец-то нашел ответ, который сохраняет различия, не предрасположен к расе и не слишком утомителен.
Первый строительный блок: поменять местами stdout и stderr:
Второй строительный блок: если бы мы хотели фильтровать (например, tee) только stderr, мы могли бы сделать это, поменяв местами stdout и stderr, фильтруя, а затем вернув обратно:
Теперь все остальное просто: мы можем добавить фильтр stdout в начале:
или в конце:
Чтобы убедить себя в том, что обе вышеперечисленные команды работают, я использовал следующее:
Выход:
и моя подсказка возвращается сразу после "
teed stderr: to stderr
", как и ожидалось.Сноска о zsh :
Вышеупомянутое решение работает в bash (и, возможно, в некоторых других оболочках, я не уверен), но не в zsh. Есть две причины, по которым он не работает в zsh:
2>&3-
не понимается zsh; это должно быть переписано как2>&3 3>&-
Так, например, мое второе решение нужно переписать для zsh as
{my_command 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stderr_filter;} 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stdout_filter
(которое работает и в bash, но ужасно многословно).С другой стороны, вы можете воспользоваться таинственным встроенным неявным teeing в zsh, чтобы получить гораздо более короткое решение для zsh, которое вообще не запускает tee:
(Я бы не догадался из документов, которые я обнаружил, что
>&1
и2>&2
- это то, что запускает неявную игру zsh; я обнаружил это методом проб и ошибок.)источник
my_function
) во вложенной фильтрации stdout / stderr? Я сделал,{ { my_function || touch failed;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter
но кажется странным создавать файл как индикатор отказа ...Используйте
script
команду в своем скрипте (скрипт man 1)Создайте оболочку оболочки (2 строки), которая устанавливает script (), а затем вызывает exit.
Часть 1: wrap.sh
Часть 2: realscript.sh
Результат:
источник
Используйте программу tee и дублируйте stderr в stdout.
источник
Я создал сценарий под названием «RunScript.sh». Содержимое этого сценария:
Я называю это так:
Это работает, но требует, чтобы скрипты приложения запускались через внешний скрипт. Это немного неуклюже.
источник
Год спустя вот старый bash-скрипт для регистрации чего угодно. Например,
teelog make ...
ведет журнал по сгенерированному имени журнала (и посмотрите также трюк с журналированием вложенныхmake
s.)источник