Как подключить stderr, а не stdout?

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

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

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

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

Ответы

Ответ 1

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

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

Подробные сведения о перенаправлении ввода-вывода во всем его разнообразии см. в главе Redirections справочного руководства Bash.

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

Ответ 2

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

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

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

Ответ 3

В Bash вы также можете перенаправить на подоболочку с помощью замены процесса:

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

В данном случае:

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

Ответ 4

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

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

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

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

Ответ 5

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

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

Далее, операторы перенаправления для каждой стороны оцениваются слева направо, а текущие настройки используются всякий раз, когда происходит дублирование дескриптора. Это важно, поскольку, поскольку сначала был настроен канал, 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 из "command" идут в /dev/null. Ничего не происходит в трубе, и, таким образом, "grep" будет закрываться, не отображая ничего на экране.

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

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

Ответ 7

Для тех, кто хочет перенаправлять 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

Ответ 8

Это перенаправит команду command1 stderr на команду command2 stdin, оставив команду command1 stdout как есть.

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

Взято из ЛДП

Ответ 9

Я только что предложил решение для отправки 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

Вероятно, это хорошая идея удалить именованные каналы позже.

Ответ 10

Я склонен делать такие вещи, как

тест композитора & >>/tmp/bob && vim/tmp/bob && rm/tmp/bob

Ответ 11

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

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