zaLinux.ru

Как использовать grep для поиска по стандартному выводу ошибок (stderr)


Некоторые программы делают вывод информации в stderr (вывод ошибок). Например,программа ffmpeg показывает метаинформацию аудио файла. Но по этой информации невозможно искать по регулярным выражениям с помощью grep (смотрите также «Регулярные выражения и команда grep»).

ffmpeg -i 01-Daemon.mp3  | grep -i Duration
    FFmpeg version SVN-r15261, Copyright (c) 2000-2008 Fabrice Bellard, et al.
      configuration: --prefix=/usr --bindir=/usr/bin 
      --datadir=/usr/share/ffmpeg --incdir=/usr/include/ffmpeg --libdir=/usr/lib
      --mandir=/usr/share/man --arch=i386 --extra-cflags=-O2 
      ...

Дело в том, что вывод ffmpeg направлен в stderr, можете убедиться в этом:

ffmpeg -i 01-Daemon.mp3 2> /dev/null

Как сделать так, чтобы grep могла читать и искать по stderr?

grep может работать только со стандартным вводом. Это канал, созданный оболочкой, который соединяет stdin grep с stdout другой команды. И оболочка может подключать только стандартный вывод к стандартному вводу.

Но варианты обойти эту проблему всё равно имеются.

Во-первых, можно просто перенаправить поток ошибок в стандартный вывод и использовать grep как обычно:

fmpeg -i 01-Daemon.mp3 2>&1 | grep -i Duration
  Duration: 01:15:12.33, start: 0.000000, bitrate: 64 kb/s

Но что если мы не хотим перенаправлять stderr в stdout?

У этой проблемы также есть решение!

В bash вы можете воспользоваться анонимными каналами:

ffmpeg -i 01-Daemon.mp3 2> >(grep -i Duration)

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

В следующей команде показан пример, когда grep ищет совпадения в потоке вывода ошибок, а стандартный вывод уничтожается:

curl -v 100.19.18.59 2> >(grep -o -i -E Unauthorized) > /dev/null


Дело в том, что HTTP заголовки команда cURL выводит в stderr, а команда grep не ищет по stderr. Но мы не может сделать просто перенаправление stderr для слияния со стандартным выводом, то есть не можем 2>&1, поскольку текст страницы может содержать слово «Unauthorized» и мы получим ложное срабатывание. Поэтому мы используем указанную выше конструкцию — вывод ошибок обрабатывается, стандартный вывод уничтожается.

Смотрите также статью «Операторы перенаправления вывода в Bash: что означает <<, <<<, < <(КОМАНДА), 2>&1 и другие».

Как в zsh передать по конвейеру только stderr в zsh

С zsh и включённой опцией mult_ios (по умолчанию включена) в:

echo hi 2>&1 1>/dev/null | cat

1> /dev/null | cat рассматривается как многократное перенаправление стандартного вывода echo.

Таким образом, стандартный вывод echo теперь перенаправлен как на /dev/null, так и на канал для cat (как при использовании tee).

Чтобы отменить это множественное перенаправление, вы можете сделать:

echo hi 2>&1 >&- > /dev/null | cat

То есть закрытие stdout (отмена конвейера) перед перенаправлением на /dev/null

Или используйте группу команд или подоболочку, например:

{echo hi 2>&1 1>/dev/null} | cat
(echo hi 2>&1 1>/dev/null) | cat

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


Или можно вообще отключить multios:

(setopt nomultios; echo hi 2>&1 > /dev/null | cat)

В качестве альтернативы вы можете использовать подстановку процесса (process substitution) вместо канала:

echo hi 2> >(cat) > /dev/null

Однако помните, что когда управление заданиями отключено (как в сценариях), процесс cat будет выполняться асинхронно (как если бы он был запущен с помощью &).


Рекомендуемые статьи:

Оставить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *