Как перенаправить и добавить stdout и stderr в файл с помощью Bash?

Чтобы перенаправить stdout в усеченный файл в Bash, я знаю, как использовать:

cmd > file.txt

Чтобы перенаправить stdout в Bash, добавив его в файл, я знаю, что нужно использовать:

cmd >> file.txt

Чтобы перенаправить и stdout, и stderr в усеченный файл, я знаю, что нужно использовать:

cmd &> file.txt

Как перенаправить оба stdout и stderr на добавление в файл? cmd &>> file.txt у меня не сработало.


person flybywire    schedule 18.05.2009    source источник
comment
Я хотел бы отметить, что & ›outfile - это код, специфичный для Bash (и других), а не переносимый. Способ стать портативным (аналогично добавляемым ответам) всегда был и остается ›outfile 2› & 1   -  person TheBonsai    schedule 18.05.2009
comment
… И порядок этого важен.   -  person Torsten Bronger    schedule 19.06.2020


Ответы (8)


cmd >>file.txt 2>&1

Bash выполняет перенаправления слева направо следующим образом:

  1. >>file.txt: Откройте file.txt в режиме добавления и перенаправьте stdout туда.
  2. 2>&1: перенаправить stderr на «туда, куда stdout сейчас идет». В данном случае это файл, открытый в режиме добавления. Другими словами, &1 повторно использует файловый дескриптор, который stdout в настоящее время использует.
person Alex Martelli    schedule 18.05.2009
comment
работает отлично! но есть ли способ разобраться в этом, или мне следует рассматривать это как атомарную конструкцию bash? - person flybywire; 18.05.2009
comment
Это простое перенаправление, операторы перенаправления оцениваются, как всегда, слева направо. ›› файл: Красный. STDOUT в файл (режим добавления) (сокращение от 1 ›› файл) 2 ›& 1: Красный. STDERR туда, куда идет stdout. Обратите внимание, что интерпретация перенаправления STDERR на STDOUT неверна. - person TheBonsai; 18.05.2009
comment
Он говорит добавить вывод (stdout, файловый дескриптор 1) в file.txt и отправить stderr (файловый дескриптор 2) в то же место, что и fd1. - person Dennis Williamson; 18.05.2009
comment
@TheBonsai, однако, что, если мне нужно перенаправить STDERR в другой файл, но добавить? Это возможно? - person arod; 03.06.2013
comment
если вы сделаете cmd >>file1 2>>file2, он должен достичь того, чего вы хотите. - person Woodrow Douglass; 07.09.2013
comment
если трубка - это то, что вам нужно, попробуйте cmd1 |& cmd2. Работает в bash v4 +. - person jpbochi; 16.01.2020
comment
Просто чтобы подчеркнуть это для таких же пользователей, как я: порядок перенаправления имеет значение, 2 ›и 1 должны быть после ›› file.txt. - person CodeBrew; 23.07.2020
comment
У меня есть file1.txt и file2.txt в текущем каталоге. Когда я запускаю: 1. ls file {1..5} .txt 1 ›res.txt 2› & 1: он дает правильный вывод в res.txt Но когда я пишу 2. ls file {1..5} .txt 1 ›Res.txt 2› res.txt: теперь он не дает корректного вывода в res.txt, здесь, в res.txt, какой-то вывод опущен по сравнению с тем, что он должен был бы быть, если бы мы не перенаправляли какой-либо поток. Почему ? - person tusharRawat; 04.08.2020

Это можно сделать двумя способами, в зависимости от вашей версии Bash.

Классический и переносимый (Bash pre-4) способ:

cmd >> outfile 2>&1

Непереносимый способ, начиная с Bash 4:

cmd &>> outfile

(аналог &> outfile)

Для хорошего стиля программирования вам следует

  • решить, является ли переносимость проблемой (затем используйте классический способ)
  • решить, вызывает ли беспокойство переносимость даже на Bash pre-4 (затем используйте классический способ)
  • независимо от того, какой синтаксис вы используете, не меняйте его в одном скрипте (путаница!)

Если ваш скрипт уже начинается с #!/bin/sh (независимо от того, задумано оно или нет), то решение Bash 4 и вообще любой код, специфичный для Bash, не подходит.

Также помните, что Bash 4 &>> - это просто более короткий синтаксис - он не вводит никаких новых функций или чего-то подобного.

Синтаксис (помимо другого синтаксиса перенаправления) описан здесь: http://bash-hackers.org/wiki/doku.php/syntax/redirection#appending_redirected_output_and_error_output

person TheBonsai    schedule 18.05.2009
comment
Я предпочитаю & ››, поскольку он соответствует & ›и ››. Также легче читать «добавить вывод и ошибки в этот файл», чем «отправить ошибки в вывод, добавить вывод в этот файл». Обратите внимание, что хотя Linux обычно имеет текущую версию bash, OS X на момент написания по-прежнему требует ручной установки bash 4 через homebrew и т. Д. - person mikemaccana; 20.05.2013
comment
Мне он больше нравится, потому что он короче и содержит только tweoi мест в строке, так что, например, zsh сделает из & ››? - person Phillipp; 17.02.2016
comment
Также важно отметить, что в задании cron вы должны использовать синтаксис pre-4, даже если в вашей системе есть Bash 4. - person hyperknot; 18.05.2017
comment
@zsero cron вообще не использует bash ... он использует sh. Вы можете изменить оболочку по умолчанию, добавив SHELL=/bin/bash к файлу crontab -e. - person Ray Foss; 05.06.2018

В Bash вы также можете явно указать свои перенаправления на разные файлы:

cmd >log.out 2>log_error.out

Добавление будет:

cmd >>log.out 2>>log_error.out
person Aaron R.    schedule 24.07.2013
comment
Перенаправление двух потоков в один и тот же файл с использованием первого варианта приведет к тому, что первый будет записывать поверх второго, перезаписывая часть или все содержимое. Используйте вместо него cmd ›› log.out 2 ›log.out. - person Orestis P.; 11.12.2015
comment
Спасибо, что уловили это; ты прав, один будет шлепать другого. Однако ваша команда тоже не работает. Я думаю, что единственный способ записать в тот же файл - тот, который был указан до cmd >log.out 2>&1. Я редактирую свой ответ, чтобы удалить первый пример. - person Aaron R.; 11.12.2015
comment
Причина, по которой cmd > my.log 2> my.log не работает, заключается в том, что перенаправления оцениваются слева направо, и > my.log говорит о создании нового файла my.log, заменяя существующие файлы и перенаправляя stdout в этот файл и после, что уже было сделано, 2> my.log оценивается, и в нем говорится: создать новый файл my.log, заменив существующие файлы, и перенаправить stderr в этот файл. Поскольку UNIX позволяет удалять открытые файлы, стандартный вывод теперь записывается в файл, который раньше назывался my.log, но с тех пор был удален. После закрытия последнего дескриптора этого файла файл contents также будет удален. - person Mikko Rantalainen; 08.07.2021
comment
С другой стороны, cmd > my.log 2>&1 работает, потому что > my.log говорит создать новый файл, my.log заменить существующие файлы и перенаправить stdout в этот файл, и после того, как это уже было сделано, 2>&1 сообщает дескриптор файла точки 2 дескриптору файла 1. И в соответствии с правилами POSIX, файл дескриптор 1 всегда stdout, а 2 всегда stderr, поэтому stderr затем указывает на уже открытый файл my.log с первого перенаправления. Обратите внимание, что синтаксис >& не создает и не изменяет фактические файлы, поэтому >>& не требуется. (Если первым перенаправлением было >> my.log, тогда файл был просто открыт в режиме добавления.) - person Mikko Rantalainen; 08.07.2021

В Bash 4 (а также ZSH 4.3.11):

cmd &>>outfile

просто из коробки

person A B    schedule 27.03.2012
comment
@all: это хороший ответ, так как он работает с bash и является кратким, поэтому я отредактировал, чтобы убедиться, что он явно упоминает bash. - person mikemaccana; 20.05.2013
comment
@mikemaccana: Ответ TheBonsai показывает решение bash 4 с 2009 года. - person jfs; 27.03.2014
comment
Почему этот ответ вообще существует, если он включен в ответ TheBonsai? Пожалуйста, подумайте об его удалении. Вы получите значок дисциплины. - person Dan Dascalescu; 14.06.2021

Это должно работать нормально:

your_command 2>&1 | tee -a file.txt

Он будет хранить все журналы в file.txt, а также выгружать их на терминал.

person Pradeep Goswami    schedule 12.12.2015
comment
Это правильный ответ, если вы тоже хотите видеть вывод в терминале. Однако изначально задавался не этот вопрос. - person Mikko Rantalainen; 15.04.2020

Попробуй это

You_command 1>output.log  2>&1

Использование вами &> x.file действительно работает в bash4. простите за это : (

Вот несколько дополнительных советов.

0, 1, 2 ... 9 - файловые дескрипторы в bash.

0 обозначает stdin, 1 обозначает stdout, 2 обозначает stderror. 3 ~ 9 резервируются для любого другого временного использования.

Любой файловый дескриптор может быть перенаправлен на другой файловый дескриптор или файл с помощью оператора > или >> (добавить).

Использование: ‹дескриптор_файла>имя_файла | & дескриптор_файла

См. Ссылку http://www.tldp.org/LDP/abs/html/io-redirection.html

person Quintus.Zhou    schedule 10.04.2014
comment
Ваш пример будет делать что-то иное, чем запрошенный OP: он перенаправит stderr You_command на stdout и stdout You_command в файл output.log. Кроме того, он не будет добавлен в файл, но перезапишет его. - person pabouk; 31.05.2014
comment
Правильно: дескриптором файла могут быть любые значения, превышающие 3 для всех остальных файлов. - person Itachi; 25.12.2014
comment
Ваш ответ показывает наиболее распространенную ошибку перенаправления вывода: перенаправление STDERR туда, где в данный момент указывает STDOUT, и только после этого перенаправление STDOUT в файл. Это не приведет к перенаправлению STDERR в тот же файл. Порядок перенаправления имеет значение. - person Jan Wikholm; 04.01.2015
comment
означает ли это, что я должен сначала перенаправить STDERROR в STDOUT, а затем перенаправить STDOUT в файл. 1 > output.log 2>&1 - person Quintus.Zhou; 04.03.2015
comment
@ Quintus.Zhou Ага. Ваша версия перенаправляет ошибку на выход и в то же время в файл. - person Alex Yaroshevich; 09.03.2015

Я удивлен, что почти за десять лет никто еще не опубликовал этот подход:

Если вы используете старые версии bash, где &>> недоступен, вы также можете:

(cmd 2>&1) >> file.txt

Это порождает подоболочку, поэтому он менее эффективен, чем традиционный подход cmd >> file.txt 2>&1, и, следовательно, не будет работать для команд, которым необходимо изменить текущую оболочку (например, cd, pushd), но этот подход кажется мне более естественным и понятным:

  1. Перенаправить stderr на stdout.
  2. Перенаправьте новый стандартный вывод, добавив его в файл.

Кроме того, круглые скобки устраняют любую двусмысленность порядка, особенно если вы хотите вместо этого передать stdout и stderr другой команде.

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

{ cmd 2>&1; } >> file.txt

(Обратите внимание, что для завершения групповой команды требуется точка с запятой (или новая строка).)

person jamesdlin    schedule 15.02.2019
comment
Эта реализация вызывает один дополнительный процесс для системы. Использование синтаксиса cmd >> file 2>&1 работает во всех оболочках и не требует дополнительного процесса для запуска. - person Mikko Rantalainen; 15.04.2020
comment
@MikkoRantalainen Я уже объяснял, что он порождает подоболочку и менее эффективен. Суть этого подхода в том, что, если эффективность не имеет большого значения (а это случается редко), этот способ легче запомнить и сложнее ошибиться. - person jamesdlin; 15.04.2020
comment
@MikkoRantalainen Я обновил свой ответ вариантом, который позволяет избежать создания подоболочки. - person jamesdlin; 28.06.2020
comment
Если вы действительно не можете вспомнить, является ли синтаксис cmd >> file 2>&1 или cmd 2>&1 >> file, я думаю, было бы проще сделать cmd 2>&1 | cat >> file вместо фигурных скобок или скобок. Для меня, как только вы поймете, что реализация cmd >> file 2>&1 буквально перенаправляет STDOUT на file с последующим перенаправлением STDERR в любой файл, на который в данный момент указывает STDOUT (что, очевидно, file после первого перенаправления), это сразу становится очевидным. в каком порядке вы ставите редиректы. UNIX не поддерживает перенаправление в поток, только в дескриптор file, на который указывает поток. - person Mikko Rantalainen; 29.06.2020

Перенаправления из внутреннего скрипта

Вы можете планировать перенаправления из самого скрипта

#!/bin/bash

exec 1>>logfile.txt
exec 2>&1

/bin/ls -ld /tmp /tnt

При запуске будет создано / добавлено logfile.txt, содержащее:

/bin/ls: cannot access '/tnt': No such file or directory
drwxrwxrwt 2 root root 4096 Apr  5 11:20 /tmp

Вход в множество разных файлов

Вы можете создать два разных файла журнала, добавив к одному общему журналу и воссоздав другой последний журнал:

#!/bin/bash

if [ -e last.log ] ;then
    mv -f last.log last.old
fi
exec 1> >(tee -a overall.log /dev/tty >last.log)
exec 2>&1

ls -ld /tnt /tmp

Запуск этого сценария приведет к

  • если last.log уже существуют, переименуйте их в last.old (перезаписав last.old, если они существуют).
  • создать новый last.log.
  • добавить все в overall.log
  • выводить все на терминал.

Простые И комбинированные бревна

#!/bin/bash

[ -e last.err ] && mv -f last.err lasterr.old
[ -e last.log ] && mv -f last.log lastlog.old

exec 2> >(tee -a overall.err combined.log /dev/tty >last.err)
exec 1> >(tee -a overall.log combined.log /dev/tty >last.log)

ls -ld /tnt /tmp

Так что у тебя есть

  • last.log файл журнала последнего запуска
  • last.err файл ошибок последнего запуска
  • lastlog.old предыдущий файл журнала выполнения
  • lasterr.old предыдущий файл ошибок выполнения
  • overall.log добавлен общий файл журнала
  • overall.err добавлен файл общей ошибки
  • combined.log добавлен объединенный файл общей ошибки и журнала.
  • все еще выводится на терминал
person F. Hauri    schedule 05.04.2021