труба stdout и stderr для двух разных процессов в сценарии оболочки? - PullRequest
52 голосов
/ 02 февраля 2012

Я делаю только пиплайн

 command1 | command2

Итак, стандартный вывод команды 1 переходит к команде 2, а стандартный вывод команды 1 - к терминалу (или везде, где находится стандартный вывод оболочки).

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

Ответы [ 6 ]

53 голосов
/ 02 февраля 2012

Используйте другой файловый дескриптор

{ command1 2>&3 | command2; } 3>&1 1>&2 | command3

Вы можете использовать до 7 других файловых дескрипторов: от 3 до 9.
Если вам нужно больше объяснений, пожалуйста, спросите, я могу объяснить; -)

Тест

{ { 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
27 голосов
/ 01 июля 2015

Принятый ответ приводит к изменению stdout и stderr.Вот метод, который сохраняет их (поскольку Googling с этой целью вызывает этот пост):

{ command 2>&1 1>&3 3>&- | stderr_command; } 3>&1 1>&2 | stdout_command

Примечание:

  • 3>&- требуется для предотвращения fd 3 от наследования command.(Поскольку это может привести к неожиданным результатам в зависимости от того, что command делает внутри.)

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

  1. Наружная часть первая:

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

    1. 2>&1 - fd 2 для command устанавливается равным fd 1 (т. Е. stderr согласно внешней части)
    2. 1>&3 - fd 1 для command установлено на то, что было fd 3 (т. Е. stdout согласно внешней части)
    3. 3>&- - fd 3 для command установлено на ничто (т. Е. закрыто )
    4. | 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 нет формы синхронизации).

13 голосов
/ 02 февраля 2012

Просто перенаправить stderr на стандартный вывод

{ command1 | command2; } 2>&1 | command3

Внимание: commnd3 также будет читать command2 стандартный вывод (если есть).
Чтобы избежать этого, вы можете отказаться от commnd2 stdout:

{ command1 | command2 >/dev/null; } 2>&1 | command3

Однако, чтобы сохранить command2 стандартный вывод (например, в терминале),
тогда, пожалуйста, обратитесь к моему другому более сложному ответу.

Тест

{ { echo -e "a\nb\nc" >&2; echo "----"; } | sed 's/$/1/'; } 2>&1 | sed 's/$/2/'

выход:

a2
b2
c2
----12
8 голосов
/ 15 марта 2018

Использование процесса замены:

command1 > >(command2) 2> >(command3)

См. http://tldp.org/LDP/abs/html/process-sub.html для получения дополнительной информации.

1 голос
/ 02 февраля 2012

Тот же эффект может быть достигнут довольно легко с 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, а не по аргументу файла, вы также можете использовать перенаправление ввода, например wc -c < err.

0 голосов
/ 21 февраля 2019

Как обычно, стандартный поток вывода, но для перенаправления stderr используется замена процесса Bash:

some_command 2> >(command of stderr) | command of stdout

Заголовок: #!/bin/bash

...