Как перенаправить stdout + stderr в один файл при сохранении потоков отдельно?
Перенаправление stdout + stderr, так что оба записываются в файл при выводе на stdout достаточно просто:
cmd 2>&1 | tee output_file
Но теперь и stdout/stderr из cmd идут на stdout. Я хотел бы написать stdout + stderr в один и тот же файл (так что упорядочение сохраняется, если cmd однопоточно), но затем все же можно также перенаправить их отдельно, примерно так:
some_magic_tee_variant combined_output cmd > >(command-expecting-stdout) 2> >(command-expecting-stderr)
Итак, объединенный_output содержит оба с сохраненным порядком, но команда-expect-stdout получает только stdout, а команда-expecting-stderr получает только stderr. В принципе, я хочу записывать stdout + stderr, но при этом stdout и stderr будут отдельно перенаправлены и переданы по каналам. Проблема с тройным подходом заключается в том, что он объединяет их вместе. Есть ли способ сделать это в bash/zsh?
Ответы
Ответ 1
От того, что я убираю, это то, что вы ищете. Сначала я сделал litte script для записи на stdout и stderr. Это выглядит так:
$ cat foo.sh
#!/bin/bash
echo foo 1>&2
echo bar
Затем я запустил его следующим образом:
$ ./foo.sh 2> >(tee stderr | tee -a combined) 1> >(tee stdout | tee -a combined)
foo
bar
Результаты в моем bash
выглядят следующим образом:
$ cat stderr
foo
$ cat stdout
bar
$ cat combined
foo
bar
Обратите внимание, что требуется флаг -a, чтобы tee
не перезаписывал другой контент tee
.
Ответ 2
{ { cmd | tee out >&3; } 2>&1 | tee err >&2; } 3>&1
Или, чтобы быть педантичным:
{ { cmd 3>&- | tee out >&3 2> /dev/null; } 2>&1 | tee err >&2 3>&- 2> /dev/null; } 3>&1
Обратите внимание, что бесполезно пытаться сохранить порядок. Это практически невозможно. Единственным решением было бы изменить "cmd" или использовать некоторые LD_PRELOAD
или gdb
hack,
Ответ 3
Порядок действительно может быть сохранен. Здесь приведен пример, который фиксирует стандартный вывод и ошибку в том порядке, в котором они сгенерированы, в файл журнала, отображая только стандартную ошибку на любом экране терминала, который вам нравится. Подстройте в соответствии с вашими потребностями.
1. Откройте два окна (оболочки)
2.Создание некоторых тестовых файлов
touch /tmp/foo /tmp/foo1 /tmp/foo2
3. В окне1:
mkfifo /tmp/fifo
</tmp/fifo cat - >/tmp/logfile
4. Затем в окне2:
(ls -l /tmp/foo /tmp/nofile /tmp/foo1 /tmp/nofile /tmp/nofile; echo successful test; ls /tmp/nofile1111) 2>&1 1>/tmp/fifo | tee /tmp/fifo 1>/dev/pts/1
Где/dev/pts/1 может быть любой вывод терминала, который вы хотите. Подселочка последовательно выполняет несколько команд "ls" и "echo", некоторые преуспевают (обеспечивают stdout), а некоторые сбой (предоставляя stderr), чтобы генерировать смешанный поток вывода и сообщений об ошибках, чтобы вы могли проверить правильность порядка в файл журнала.
Ответ 4
Вот как я это делаю:
exec 3>log ; example_command 2>&1 1>&3 | tee -a log ; exec 3>&-
Пример работы
bash$ exec 3>log ; { echo stdout ; echo stderr >&2 ; } 2>&1 1>&3 | \
tee -a log ; exec 3>&-
stderr
bash$ cat log
stdout
stderr
Вот как это работает:
exec 3>log
устанавливает дескриптор файла 3 для перенаправления в файл с именем log, до дальнейшего уведомления.
example_command
, чтобы сделать этот рабочий пример, я использовал { echo stdout ; echo stderr >&2 ; }
. Или вы можете использовать ls /tmp doesnotexist
для предоставления вывода.
Вам нужно перейти к трубе |
в этот момент, потому что bash делает это сначала. Труба устанавливает трубку и перенаправляет дескриптор файла 1 в этот канал. Итак, теперь STDOUT входит в трубу.
Теперь мы можем вернуться к следующему в нашей интерпретации слева направо: 2>&1
это говорит о том, что ошибки из программы состоят в том, чтобы перейти туда, где в настоящее время указывается STDOUT, то есть в только что сконфигурированный канал.
1>&3
означает, что STDOUT перенаправляется в файловый дескриптор 3, который мы ранее установили для вывода в файл log
. Поэтому STDOUT из команды просто входит в файл журнала, а не в терминал STDOUT.
tee -a log
выводит его из канала (который вы сейчас будете помнить об ошибках из команды) и выводит его в STDOUT, а также добавляет его в файл log
.
exec 3>&-
закрывает дескриптор файла.
Ответ 5
Комментарий Виктора Сергиенко - это то, что сработало для меня, добавив, что exec перед ним делает эту работу для всего script (вместо того, чтобы поместить его после отдельных команд)
exec 2> >(tee -a output_file >&2) 1> >(tee -a output_file)