Как перенаправить stderr в pipe, сохраняя stdout
Узнайте, как перенаправить stderr в pipe, сохраняя stdout в Unix/Linux с помощью подстановки процессов. Устраните временные файлы с помощью этой простой техники: команда 2> >(grep шаблон). Полное руководство с примерами.
Как перенаправить stderr в pipe, сохраняя stdout в командах оболочки Unix/Linux?
У меня есть программа, которая записывает информацию в stdout и stderr, и мне нужно обработать stderr с помощью grep, оставив stdout в покое.
Используя временный файл, это можно сделать в два шага:
команда > /dev/null 2> temp.file
grep 'что-то' temp.file
Но как этого можно достичь без временных файлов, используя одну команду и pipe?
Вы можете перенаправить stderr в канал, сохраняя stdout, с помощью подстановки процесса (process substitution) с синтаксисом 2> >(command). Это позволяет отправлять stderr в конвейер без влияния на stdout, устраняя необходимость во временных файлах. Например: command 2> >(grep 'something') передаст stderr в grep, оставив stdout без изменений.
Содержание
- Понимание подстановки процессов
- Основной синтаксис и примеры
- Альтернативные методы с дескрипторами файлов
- Практические случаи использования и примеры
- Сравнение методов
- Распространенные проблемы и решения
- Заключение
Понимание подстановки процессов
Подстановка процессов — это мощная функция оболочки, позволяющая рассматривать вывод команды как временный файл. Синтаксис 2> >(command) специально перенаправляет stderr (дескриптор файла 2) в процесс, оставляя stdout (дескриптор файла 1) без изменений.
Подстановка процесса >(command) создает именованный канал (FIFO), который оболочка управляет автоматически. Когда вы перенаправляете stderr в эту подстановку процесса, команда получает ввод stderr через свой stdin, в то время как исходная команда продолжает записывать в stdout в обычном режиме.
Согласно TLDP Advanced Bash-Scripting Guide, “Перенаправление stdout одной команды в stdin другой — это мощная техника. Но что, если вам нужно перенаправить stdout нескольких команд? Здесь и приходит на помощь подстановка процессов”.
Основной синтаксис и примеры
Основной синтаксис для перенаправления stderr в канал при сохранении stdout:
команда 2> >(обрабатывающая_команда)
Для вашего конкретного случая использования с grep:
команда 2> >(grep 'что-то')
Эта команда будет:
- Выполнять вашу исходную команду
- Отправлять весь вывод stderr в grep для фильтрации
- Полностью оставлять вывод stdout без изменений и доступным для других операций
Дополнительные вариации
Вы можете расширить это с помощью нескольких обрабатывающих команд:
# Отправить stderr в grep, одновременно выводя в консоль
команда 2> >(grep 'что-то' | tee /dev/stderr)
# Сложная обработка с sed
команда 2> >(sed -e 's/^/ОШИБКА: /' | logger -t myapp)
Альтернативные методы с дескрипторами файлов
Хотя подстановка процессов является наиболее прямым подходом, вы также можете достичь этого результата с помощью манипуляций с дескрипторами файлов:
Метод 1: Использование exec для сохранения дескрипторов файлов
exec 3>&1 # Сохранить текущий stdout в дескриптор файла 3
команда 2>&1 1>&3 | grep 'что-то' # Поменять stderr и stdout местами, затем передать в канал
exec 3>&- # Очистить дескриптор файла 3
Метод 2: Продвинутое переключение дескрипторов файлов
Как показано в этом ответе на Stack Overflow, вы можете использовать:
{
команда 2>&1 1>&3 3>&- | команда_stderr
} 3>&1 1>&2 | команда_stdout
Этот подход более сложный, но обеспечивает больший контроль над потоками stdout и stderr.
Практические случаи использования и примеры
Пример 1: Фильтрация сообщений об ошибках
# Найти все файлы, но показывать только ошибки отказа в доступе
find /usr 2> >(grep 'Permission denied')
Это выведет список всех файлов в каталоге /usr, но отобразит только сообщения об ошибках “Permission denied”, в то время как обычные списки файлов будут направлены в stdout.
Пример 2: Логирование ошибок с продолжением обработки
# Запустить приложение, логировать ошибки в syslog, оставить stdout для пользователя
myapp 2> >(logger -t myapp -p user.error)
Пример 3: Извлечение конкретной информации об ошибках
# Извлечь только неудачные подключения из сетевой команды
curl -s https://example.com 2> >(grep 'failed to connect')
Пример 4: Множественная обработка ошибок
# Обрабатывать ошибки разными командами
команда 2> >(grep 'critical' > critical.log) 2> >(grep 'warning' > warning.log)
Сравнение методов
| Метод | Синтаксис | Плюсы | Минусы | Лучше всего подходит для |
|---|---|---|---|---|
| Подстановка процессов | 2> >(команда) |
Простой, читаемый, не требует очистки | Недоступен во всех оболочках | Большинство случаев использования, современные оболочки |
| Переключение дескрипторов файлов | `exec 3>&1; команда 2>&1 1>&3 | grep` | Работает в старых оболочках | Более сложный, требует очистки |
| **Оператор | &** | `команда | & grep` | Очень лаконичный |
| Отдельные перенаправления | 2> >(команда1) > >(команда2) |
Полный контроль над обоими потоками | Самый сложный синтаксис | Продвинутая обработка потоков |
Метод подстановки процессов обычно предпочитается за его простоту и читаемость, как отмечено в обсуждениях на Server Fault.
Распространенные проблемы и решения
Проблема 1: Порядок подстановки процессов
Если вы настраиваете несколько подстановок процессов, порядок имеет значение:
# НЕПРАВИЛЬНО - обработка stderr получает префикс "stdout: "
exec > >(while read line; do echo " stdout: $line"; done)
exec 2> >(while read line; do echo " stderr: $line"; done)
# ПРАВИЛЬНО - Подстановки процессов в правильном порядке
exec 2> >(while read line; do echo " stderr: $line"; done)
exec > >(while read line; do echo " stdout: $line"; done)
Проблема 2: Совместимость с оболочкой
Подстановка процессов недоступна во всех оболочках. Она работает в:
- Bash (версия 3.0+)
- Zsh
- Ksh
Для совместимости со старыми оболочками, такими как базовый sh, используйте метод с дескрипторами файлов.
Проблема 3: Проблемы с буферизацией
Иногда буферизованный вывод может вызывать задержки. Чтобы обеспечить немедленный вывод:
# Используйте stdbuf или unbuffer для немедленного вывода
stdbuf -o0 -e0 команда 2> >(grep 'что-то')
Заключение
Наиболее эффективный способ перенаправить stderr в канал, сохраняя stdout в Unix/Linux, — это подстановка процессов с использованием синтаксиса 2> >(команда). Этот подход:
- Полностью устраняет временные файлы
- Сохраняет stdout без изменений для других операций
- Предоставляет чистый, читаемый синтаксис, который легко понять
- Работает с любой обрабатывающей командой, такой как grep, sed, awk и т.д.
Для вашего конкретного случая использования решение простое:
команда 2> >(grep 'что-то')
Эта однострочная команда выполняет именно то, что вам нужно — обрабатывает stderr с помощью grep, оставляя stdout без изменений, все без создания каких-либо временных файлов. Функция подстановки процессов особенно мощна для сценариев оболочки и операций командной строки, где необходимо обрабатывать выходные потоки отдельно.
Источники
- TLDP Advanced Bash-Scripting Guide - Подстановка процессов
- Unix & Linux Stack Exchange - Подстановка процессов и канал
- Stack Overflow - Перенаправление stdout и stderr в два разных процесса
- Server Fault - Перенаправление stderr и stdout в разные команды
- SS64 - Перенаправление и подстановка процессов
- Unix & Linux Stack Exchange - Использование подстановки процессов, только отправка stderr в процесс