Некоторые программы делают вывод информации в 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 будет выполняться асинхронно (как если бы он был запущен с помощью &).
Связанные статьи:
- Как использовать кавычки в регулярных выражениях grep (63.5%)
- Команда grep: опции, регулярные выражения и примеры использования (62.2%)
- Как обработать каждую строку, полученную от команды grep (56.6%)
- Лучшие терминальные мультиплексные инструменты (51.4%)
- Как присвоить переменной вывод команды в Bash (51.4%)
- Ошибка «-bash: sudo: command not found» - не найдена команда sudo (РЕШЕНО) (RANDOM - 50%)