Потоки 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 — отдельный машинный сигнал; отсутствие текста ошибки не доказывает успех.