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

Pipe stdout и stderr для двух разных процессов в оболочке script?

У меня есть трубопровод, который делает только

 command1 | command2

Итак, stdout команды1 переходит в command2, а stderr команды1 переходит к терминалу (или где бы то ни было, где находится stdout оболочки).

Как я могу передать stderr команды1 в третий процесс (command3), в то время как stdout все еще собирается command2?

4b9b3361

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

Объясненные детали:

  • Внешняя часть:

    • 3>&1 - fd 3 для { ... } установлено значение fd 1 (т.е. stdout)
    • 1>&2 - fd 1 для { ... } установлено значение fd 2 (т.е. stderr)
    • | stdout_command - fd 1 (был stdout) передан через stdout_command
  • Внутренняя часть наследует дескрипторы файлов из внешней части:

    • 2>&1 - fd 2 для command установлено значение fd 1 (т.е. stderr в соответствии с внешней частью)
    • 1>&3 - fd 1 для command установлено значение fd 3 (т.е. stdout в соответствии с внешней частью)
    • 3>&- - fd 3 для command не установлен (т.е. закрыт)
    • | stderr_command - fd 1 (был stderr) пропущен через stderr_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.