Потоки 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 на ноль и объясните, почему содержимое потоков при этом не изменилось.

Что важно запомнить

Источники