Как трубить stderr, а не stdout?

у меня есть программа, которая записывает информацию stdout и stderr, и мне нужно grep через то, что происходит stderr, а без учета stdout.

Я могу, конечно, сделать это в 2 шага:

command > /dev/null 2> temp.file
grep 'something' temp.file

но я бы предпочел сделать это без временных файлов. Есть ли умные трюки с трубами?

10 ответов


сначала перенаправьте stderr в stdout-канал; затем перенаправьте stdout в /dev/null (без изменения, куда идет stderr):

command 2>&1 >/dev/null | grep 'something'

подробную информацию о перенаправлении ввода-вывода во всем его разнообразии см. В главе редиректы в справочном руководстве Bash.

обратите внимание, что последовательность перенаправлений ввода-вывода интерпретируется слева направо, но каналы настраиваются до интерпретации перенаправлений ввода-вывода. Дескрипторов файлов, таких как 1 и 2 ссылки откройте описания файлов. Операция 2>&1 делает файловый дескриптор 2 aka stderr ссылаться на то же самое открытое описание файла, как файловый дескриптор 1 aka stdout в настоящее время ссылается на (см. dup2() и open()). Операция >/dev/null затем изменяет файловый дескриптор 1 так, что он ссылается на открытое описание файла для /dev/null, но это не меняет того факта, что файловый дескриптор 2 ссылается на описание открытого файла, который первоначально был файловым дескриптором 1 указывая на-а именно, на трубу.


или для замены вывода из stderr и stdout над использованием: -

command 3>&1 1>&2 2>&3

при этом создается новый файл дескриптора (3) и закрепляет его на одном месте, а 1 (стандартный вывод), затем назначает ФД 1 (стандартный вывод) в том же месте, ФД 2 (stderr) и, наконец, назначает ФД 2 (поток stderr) в том же месте, ФД 3 (стандартный вывод). Stderr теперь доступен как stdout и старый stdout, сохраненный в stderr. Это может быть излишним, но, надеюсь, дает более подробную информацию о дескрипторах файлов bash (есть 9 доступных для каждого процесс.)


в Bash вы также можете перенаправить на подсеть, используя подмена процесса:

command > >(stdlog pipe)  2> >(stderr pipe)

по делу:

command 2> >(grep 'something') >/dev/null

объединение лучших из этих ответов, если вы это сделаете:

command 2> >(grep -v something 1>&2)

...тогда все stdout сохраняется как stdout и все stderr сохраняется как stderr, но вы не увидите никаких строк в stderr, содержащих строку "что-то".

это имеет уникальное преимущество не реверсировать или отбрасывать stdout и stderr, ни разбивая их вместе, ни используя какие-либо временные файлы.


гораздо проще визуализировать вещи, если вы думаете о том, что на самом деле происходит с "перенаправлениями" и "трубами"."Перенаправления и каналы в bash делают одну вещь: изменяют, где дескрипторы файла процесса 0, 1 и 2 указывают на (см. /proc/[pid]/fd/*).

когда труба или оператор "|" присутствует в командной строке, первое, что должно произойти, это то, что bash создает fifo и указывает FD 1 команды левой стороны на этот fifo, и указывает FD 0 команды правой стороны на же ФИФО.

далее, операторы перенаправления для каждой стороны оцениваются слева направо, и текущие настройки используются всякий раз, когда происходит дублирование дескриптора. Это важно, потому что, поскольку труба была настроена первой, FD1 (левая сторона) и FD0 (правая сторона) уже изменены от того, что они могли бы быть обычно, и любое дублирование этих будет отражать этот факт.

поэтому, когда вы вводите что-то вроде следующий:

command 2>&1 >/dev/null | grep 'something'

вот что происходит, по порядку:

  1. создается труба (fifo). "команда FD1" указывает на этот канал. "grep FD0" также указывает на эту трубу
  2. "команда FD2" указывает на то, где "команда FD1" в настоящее время указывает (труба)
  3. "command FD1" указывает на /dev / null

Итак, все выходные данные, которые" команда "записывает в свой FD 2 (stderr), пробиваются в канал и считываются "grep" с другой сторона. Весь вывод, который" команда " записывает в свой FD 1 (stdout), делает свой путь к /dev/null.

Если вместо этого вы выполните следующую:

command >/dev/null 2>&1 | grep 'something'

вот что происходит:

  1. создается канал и на него указывают "command FD 1" и "grep FD 0"
  2. "команда FD 1" указывает на /dev / null
  3. "команда FD 2" указывает на то, где FD 1 в настоящее время указывает (/dev/null)

Итак, все stdout и stderr от "команда" перейти к /dev / null. Ничто не идет в трубу, и, таким образом, "grep" закроется, не отображая ничего на экране.

также обратите внимание, что перенаправления (файловые дескрипторы) могут быть только для чтения ( ) или чтения-записи ().

Примечание. Пишет ли программа что-то в FD1 или FD2, полностью зависит от программиста. Хорошая практика программирования диктует, что сообщения об ошибках должны идти в FD 2 и нормальный вывод в FD 1, но вы часто найдете небрежный программирование, которое смешивает два или иным образом игнорирует соглашение.


вы используете bash? Если это так:

command >/dev/null |& grep "something"

http://www.gnu.org/software/bash/manual/bashref.html#Pipelines


для тех, кто хочет перенаправить stdout и stderr постоянно в файлы, grep на stderr, но сохранить stdout для записи сообщений в tty:

# save tty-stdout to fd 3
exec 3>&1
# switch stdout and stderr, grep (-v) stderr for nasty messages and append to files
exec 2> >(grep -v "nasty_msg" >> std.err) >> std.out
# goes to the std.out
echo "my first message" >&1
# goes to the std.err
echo "a error message" >&2
# goes nowhere
echo "this nasty_msg won't appear anywhere" >&2
# goes to the tty
echo "a message on the terminal" >&3

это перенаправит command1 stderr на command2 stdin, оставив command1 stdout как есть.

exec 3>&1
command1 2>&1 >&3 3>&- | command2 3>&-
exec 3>&-

принято от LDP


Я только что придумал решение для отправки stdout до одной команды и stderr к другому, используя именованные каналы.

здесь идет.

mkfifo stdout-target
mkfifo stderr-target
cat < stdout-target | command-for-stdout &
cat < stderr-target | command-for-stderr &
main-command 1>stdout-target 2>stderr-target

вероятно, это хорошая идея, чтобы удалить именованные трубы после этого.


Я пытаюсь следовать, найти его работу, а также,

command > /dev/null 2>&1 | grep 'something'