Pipe stdout и stderr для двух разных процессов в оболочке script?
У меня есть трубопровод, который делает только
command1 | command2
Итак, stdout команды1 переходит в command2, а stderr команды1 переходит к терминалу (или где бы то ни было, где находится stdout оболочки).
Как я могу передать stderr команды1 в третий процесс (command3
), в то время как stdout все еще собирается command2?
Ответы
Ответ 1
Использовать другой файловый дескриптор
{ command1 2>&3 | command2; } 3>&1 1>&2 | command3
Вы можете использовать до 7 других дескрипторов файлов: от 3 до 9.
Если вы хотите больше объяснений, спросите, я могу объяснить; -)
Test
{ { echo a; echo >&2 b; } 2>&3 | sed >&2 's/$/1/'; } 3>&1 1>&2 | sed 's/$/2/'
выход:
b2
a1
Пример
Создайте два файла журнала:
1. stderr
только
2. stderr
и stdout
{ { { command 2>&1 1>&3; } | tee err-only.log; } 3>&1; } > err-and-stdout.log
Если command
- echo "stdout"; echo "stderr" >&2
, тогда мы можем проверить его следующим образом:
$ { { { echo out>&3;echo err>&1;}| tee err-only.log;} 3>&1;} > err-and-stdout.log
$ head err-only.log err-and-stdout.log
==> err-only.log <==
err
==> err-and-stdout.log <==
out
err
Ответ 2
Принятый ответ приводит к реверсированию stdout
и stderr
. Здесь метод, который их сохраняет (поскольку Googling с этой целью поднимает этот пост):
{ command 2>&1 1>&3 3>&- | stderr_command; } 3>&1 1>&2 | stdout_command
Примечание:
-
3>&-
требуется для предотвращения наследования fd 3 на command
. (Так как это может привести к неожиданным результатам в зависимости от того, что внутри command
).
Объясненные детали:
Пример:
foo() {
echo a
echo b >&2
echo c
echo d >&2
}
{ foo 2>&1 1>&3 3>&- | sed -u 's/^/err: /'; } 3>&1 1>&2 | sed -u 's/^/out: /'
Вывод:
out: a
err: b
err: d
out: c
(Порядок a -> c
и b -> d
всегда будет неопределенным, потому что нет формы синхронизации между stderr_command
и stdout_command
.)
Ответ 3
Просто перенаправить stderr в stdout
{ command1 | command2; } 2>&1 | command3
Внимание: commnd3
также будет читать command2
stdout (если есть).
Чтобы этого избежать, вы можете отказаться от commnd2
stdout:
{ command1 | command2 >/dev/null; } 2>&1 | command3
Однако сохранить command2
stdout (например, в терминале),
то, пожалуйста, обратитесь к моему другому ответу более сложным.
Test
{ { echo -e "a\nb\nc" >&2; echo "----"; } | sed 's/$/1/'; } 2>&1 | sed 's/$/2/'
выход:
a2
b2
c2
----12
Ответ 4
Такой же эффект можно выполнить довольно легко с помощью fifo. Я не знаю о синтаксисе прямого трубопровода для этого (хотя было бы замечательно видеть его). Так вы можете сделать это с помощью fifo.
Во-первых, что-то, что печатает как для stdout
, так и stderr
, outerr.sh
:
#!/bin/bash
echo "This goes to stdout"
echo "This goes to stderr" >&2
Тогда мы можем сделать что-то вроде этого:
$ mkfifo err
$ wc -c err &
[1] 2546
$ ./outerr.sh 2>err | wc -c
20
20 err
[1]+ Done wc -c err
Таким образом вы сначала настроите прослушиватель для stderr
вывода, и он блокируется до тех пор, пока у него нет записи, что происходит в следующей команде, используя синтаксис 2>err
. Вы можете видеть, что каждый wc -c
получил 20 символов ввода.
Не забудьте очистить fifo после того, как вы закончите, если вы не хотите, чтобы он зависал (т.е. rm
). Если другой команде требуется ввод на stdin
, а не файл arg, вы можете использовать перенаправление ввода, например, wc -c < err
.