Вопрос или проблема
Я перенаправляю stdout и stderr в один и тот же файл, используя > и >> соответственно:
rsync -a --exclude cache/ src_folder/ target_folder/ 1>out_err.log 2>>out_err.log
Однако ошибка стандартного вывода не записывается в файл. Что происходит с ошибкой стандартного вывода, когда используется >> вместо > для перенаправления обоих в один и тот же файл?
Я собирался записать stdout в новый файл, а затем добавить stderr в тот же файл.
Проблема с использованием 1>out_err.log 2>>out_err.log
заключается в том, что это создает два отдельных открытых дескриптора файла для вашего целевого файла: один для stdout и один для stderr. Точный результат в этом случае зависит от порядка, в котором записываются сообщения, так же, как если бы две отдельные команды записывали в этот файл. Если stderr записывает в файл первым, то когда stdout начнет записывать, он сотрет все, что было записано ранее. (Более того, “первым” может не совпадать с тем, что вы увидели бы, если бы выполнили ту же команду в консоли.)
Обычный и безопасный подход в этой ситуации — сначала перенаправить stdout в ваш желаемый файл лога, а затем использовать 2>&1
, чтобы перенаправить stderr в тот же открытый дескриптор файла, что и stdout.
rsync -a --exclude cache/ src_folder/ target_folder/ > out_err.log 2>&1
Я собирался записать stdout в новый файл, а затем добавить stderr в тот же файл.
Утилита rsync
чередует свой stdout и stderr, поэтому в этой ситуации вам нужно отделить их, сохраняя stderr до тех пор, пока вы не запишите все из stdout. Утилита sponge
(из moreutils
) позволит вам сделать это.
rsync… 2>&1 >out_err.log | sponge -a out_err.log
Здесь мы записываем stdout напрямую из rsync
, передавая stderr во stdin sponge
. Этот инструмент принимает все из stdin и ничего не записывает (удерживая все в памяти), пока трубопровод не будет закрыт, после чего все будет добавлено в файл лога.
Ответ или решение
Чтобы корректно перенаправить стандартный вывод (stdout) и стандартный вывод ошибок (stderr) в один и тот же файл, важно понимать, как работает механизм перенаправления в оболочке Unix/Linux.
Ваше текущее решение:
rsync -a --exclude cache/ src_folder/ target_folder/ 1>out_err.log 2>>out_err.log
не дает ожидаемого результата, потому что вы создаете два отдельных дескриптора файлов для одного и того же файла out_err.log
. Таким образом, когда stderr
и stdout
записываются в файл, порядок их записи становится непредсказуемым: какое-то сообщение из stderr
может быть перезаписано сообщением из stdout
, и наоборот.
Чтобы решить эту проблему, правильнее будет перенаправить stdout
в файл, а затем перенаправить stderr
на тот же дескриптор файла, что и stdout
. Это делается следующим образом:
rsync -a --exclude cache/ src_folder/ target_folder/ > out_err.log 2>&1
Таким образом, вы записываете stdout
в out_err.log
, а затем перенаправляете stderr
(2
) в тот же поток, что и stdout
(1
), что гарантирует, что оба вывода будут записываться в правильном порядке.
Однако, если ваша цель — сначала записать stdout
в новый файл, а затем добавить stderr
в тот же файл, необходимо использовать другой подход. Поскольку rsync
может выводить сообщения как в stdout
, так и в stderr
, вам следует сохранить stderr
, пока не будет завершена запись в stdout
.
Для этого вы можете использовать утилиту sponge
, которая входит в пакет moreutils
. Она позволяет сначала считывать данные из stdin
, а записывать их в файл только после закрытия потока. Вот как это можно сделать:
rsync -a --exclude cache/ src_folder/ target_folder/ > >(tee out_err.log) 2>&1 | sponge -a out_err.log
Сначала данные из stdin
передаются в tee
, который записывает stdout
в out_err.log
. Затем через | sponge -a out_err.log
происходит запуск дополнительного процесса, который будет дожидаться завершения команды и затем добавлять stderr
в тот же файл.
Таким образом, описанный вами сценарий будет успешно выполнен, и обе части вывода будут корректно собраны в одном файле.