Три потока: stdin, stdout, stderr

Понимаем, откуда программа берёт ввод и куда отправляет вывод и ошибки.

Стандартные потоки — это три канала, которые есть у любой программы: ввод (stdin), вывод (stdout) и ошибки (stderr).

Зачем разделять вывод и ошибки

Каждая программа в Unix по умолчанию получает три открытых потока с номерами-дескрипторами: 0 — stdin (откуда читать), 1 — stdout (куда писать результат), 2 — stderr (куда писать ошибки). Разделение нужно, чтобы можно было отдельно собрать результат и отдельно — диагностику.

          ┌──────────────┐
stdin 0 ──▶│   программа  │──▶ stdout 1 (результат)
          └──────────────┘──▶ stderr 2 (ошибки)

Поток ошибок отдельно от результата

Рассмотрим команду, которая что-то находит, а что-то нет:

ls /etc /not-exist

Вывод:

ls: cannot access '/not-exist': No such file or directory
/etc:
hosts
passwd

Здесь строка с ошибкой ушла в stderr (поток 2), а список файлов — в stdout (поток 1). На экране они смешаны, но это разные каналы, и их можно развести.

Перенаправление потока ошибок

Чтобы отправить только ошибки в файл, перенаправляют дескриптор 2:

ls /etc /not-exist 2> errors.log
cat errors.log

Вывод:

ls: cannot access '/not-exist': No such file or directory

Запись 2> значит «перенаправить поток 2 (stderr)». В файл попали только ошибки, а нормальный вывод остался на экране. Чтобы выбросить ошибки совсем, направляют их в «чёрную дыру»: 2> /dev/null.

Как работает под капотом

Дескриптор — это просто целое число, индекс в таблице открытых файлов процесса. По умолчанию все три указывают на терминал, поэтому вы видите и вывод, и ошибки в одном окне. Когда вы пишете 2> file, Bash до запуска программы подменяет дескриптор 2 на этот файл. Сама программа не знает, куда реально идёт поток — она просто пишет в номер 2.

Частые ошибки

  • Ждать, что ошибки попадут в файл при обычном >. cmd > out.txt ловит только stdout; ошибки всё равно вылезут на экран.
  • Пробел между 2 и >. Нужно 2> слитно; 2 > Bash поймёт иначе.
  • Считать /dev/null файлом с данными. Это устройство-«пустота»: всё записанное туда исчезает, чтение даёт пусто.

Итог

  • У программы три потока: stdin (0), stdout (1), stderr (2).
  • Результат идёт в stdout, ошибки — в stderr; их можно разделять.
  • 2> file перенаправляет ошибки, 2> /dev/null их выбрасывает.
Проверьте себя
1. Какой дескриптор соответствует потоку ошибок stderr?
A0
B1
C2
D3
2. Куда попадут ошибки после команды cmd 2> err.log?
AТолько обычный вывод в err.log
BТолько ошибки (stderr) в err.log
CВсё вместе в err.log
DНичего
3. Что произойдёт с данными, записанными в /dev/null?
AСохранятся в файл
BБудут отброшены — это устройство-пустота
CВыведутся на экран
DВызовут ошибку