Как трубить 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 (стандартный вывод). Устройство теперь доступно как 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'
вот что происходит, по порядку:
- создается труба (fifo). "команда FD1" указывает на эту трубу. "grep FD0" также указывает на эту трубу
- "команда FD2" указывает на то, где" команда FD1 " в настоящее время указывает (труба)
- "команда FD1" указывает на /dev / null
Итак, все выходные данные, которые" команда "записывает в свой FD 2 (stderr), пробиваются к трубе и читаются "grep" на другом сторона. Все выходные данные, которые" команда " записывает в свой FD 1 (stdout), попадают в /dev/null.
Если вместо этого вы выполните следующую:
command >/dev/null 2>&1 | grep 'something'
вот что происходит:
- создается канал и на него указывают "команда FD 1" и "grep FD 0"
- "команда FD 1" указывает на /dev / null
- "команда 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
Это, вероятно, хорошая идея, чтобы удалить именованные каналы опосля.