Как перенаправить stdout+stderr в один файл, сохраняя потоки отдельно?
Перенаправление stdout+stderr таким образом, что оба записываются в файл, а вывод на stdout все же достаточно прост:
cmd 2>&1 | tee output_file
Но тогда теперь оба stdout / stderr из cmd приходят на стандартный вывод. Я хотел бы написать stdout+stderr в тот же файл (так что порядок сохраняется, предполагая, что cmd является однопоточным), но затем все еще можно перенаправить их отдельно, что-то вроде этого:
some_magic_tee_variant combined_output cmd > >(command-expecting-stdout) 2> >(command-expecting-stderr)
Таким образом, комбинированный_отчет содержит оба параметра с сохраненным порядком, но команда-ожидающий-stdout получает только stdout, а команда-ожидающий-stderr получает только stderr. По сути, я хочу войти в stdout+stderr, при этом позволяя stdout и stderr отдельно перенаправляться и передаваться по конвейеру. Проблема с тройным подходом состоит в том, что он объединяет их. Есть ли способ сделать это в bash/zsh?
5 ответов
Из того, что я понимаю, это то, что вы ищете. Сначала я написал небольшой скрипт для написания на 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
содержание.
Вот как я это делаю:
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>&-
закрывает дескриптор файла 3.
Комментарий Виктора Сергиенко - это то, что сработало для меня, добавление exec в начало делает эту работу для всего сценария (вместо того, чтобы ставить его после отдельных команд)
exec 2> >(tee -a output_file >&2) 1> >(tee -a output_file)
{ { 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
хак,
Порядок действительно может быть сохранен. Вот пример, который фиксирует стандартный вывод и ошибку в порядке их генерации в лог-файл, отображая при этом только стандартную ошибку на любом экране терминала, который вам нравится. Настроить, чтобы удовлетворить ваши потребности.
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), чтобы создать смешанный поток выходных сообщений и сообщений об ошибках, чтобы вы могли проверить правильность упорядочения в файл журнала.