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