Подтвердить что ты не робот

Как перенаправить 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?

4b9b3361

Ответ 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)