Потоки stdin, stdout и stderr: как команды обмениваются данными
Автор: Казачкин Даниил Михайлович · Обновлено
Программа в терминале обычно получает данные из стандартного ввода и отдаёт два независимых потока: обычный результат в stdout, а диагностику в stderr. Эта граница позволяет соединять небольшие утилиты и при этом не смешивать полезные данные с сообщениями об ошибках.
Три канала одного процесса
При запуске shell открывает для процесса файловые дескрипторы 0, 1 и 2. Номер 0 соответствует stdin, 1 — stdout, 2 — stderr. Слово «файл» здесь шире обычного документа: дескриптор может указывать на терминал, канал pipe, сетевой сокет или перенаправленный файл.
Команда wc -l читает строки из stdin, если путь не передан. printf пишет результат в stdout. Ошибка ls уходит в stderr, поэтому её можно наблюдать отдельно.
printf '%s\n' alpha beta gamma | wc -l
ls /definitely-missing
ls /definitely-missing >/tmp/list.out 2>/tmp/list.err
wc -c /tmp/list.out /tmp/list.errВ последнем примере stdout пуст, а текст ошибки находится в /tmp/list.err. Программа может сохранить машинно читаемый результат в одном месте и оставить оператору подробную причину сбоя в другом.
Код завершения дополняет текст
Поток сообщения не говорит автоматически, успешно ли закончилась команда. Shell отдельно хранит exit status: ноль означает успех, другое значение — определённый вид неуспеха. Проверять нужно сразу после интересующей команды.
grep -q '^root:' /etc/passwd
status=$?
printf 'grep status=%s\n' "$status"grep -q ничего не печатает при найденной строке, но сообщает результат кодом. Скрипт, который ищет слово «error» только в stdout и игнорирует status, способен принять тихий сбой за успех.
Ошибка и диагностика
Частая ошибка — направить stdout в файл и ожидать, что туда попадут ошибки: command >result.log. Оператор > меняет только дескриптор 1. Для проверки временно разделите каналы: command >/tmp/out 2>/tmp/err, затем сравните размеры и содержимое файлов. Если нужен общий журнал, порядок перенаправлений тоже важен и разбирается отдельно в уроке про redirect.
Практика
Запустите команду, которая печатает одну строку результата и одну строку ошибки: sh -c 'echo result; echo problem >&2; exit 7'. Сохраните потоки в разные файлы, выведите код завершения, затем убедитесь, что в каждом файле ровно одна ожидаемая строка. Измените exit status на ноль и объясните, почему содержимое потоков при этом не изменилось.
Что важно запомнить
- stdin, stdout и stderr — независимые каналы процесса, а не просто текст на экране.
- stderr следует отделять от данных, которые будет читать другая программа.
- Exit status — отдельный машинный сигнал; отсутствие текста ошибки не доказывает успех.
Частые вопросы
Почему stderr виден, хотя stdout перенаправлен?
Потому что > без номера меняет только stdout. Stderr продолжает указывать на терминал, пока вы явно не перенаправите дескриптор 2.
Всегда ли stdout является текстом?
Нет. Программа может передавать бинарные байты. Просмотр такого потока через обычный терминал способен испортить отображение, поэтому формат нужно знать заранее.
Можно ли определить успех только по stderr?
Нельзя. Некоторые программы пишут предупреждения в stderr и завершаются успешно, а тихие режимы могут не печатать ничего при ошибке. Проверяйте документированный exit status.