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


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

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

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

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

10 786

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 (стандартный вывод). Устройство теперь доступно как 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/*).

Когда a труба или оператор " | " присутствует в командной строке, первое, что должно произойти, это то, что 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. "команда FD1" указывает на /dev / null

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

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

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

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

  1. создается канал и на него указывают "команда 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

Это позволит перенаправить stderr в КОМАНДА1 команда2 устройства stdin, оставляя КОМАНДА1 вывод как это.

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'