open atlas
↑ К треку
Командная строка CLI · 03 · 01

stdin, stdout и stderr

Каждый процесс стартует с тремя открытыми файловыми дескрипторами: stdin (fd 0) читает ввод, stdout (fd 1) пишет обычный вывод, stderr (fd 2) пишет ошибки. Понимание этих трёх потоков — фундамент конвейеров оболочки.

CLI Основы ◷ 18 min
Уровень
ОсновыJuniorMiddleSenior

Каждая запускаемая тобой команда — ls, cat, grep — откуда-то читает и куда-то пишет. Ты видел, как текст появляется в терминале при запуске команд, но, вероятно, не задумывался, куда этот текст идёт и почему ошибки оказываются вперемешку с обычным выводом. Оболочка даёт каждому процессу три открытых канала с момента его запуска. Понимание этих трёх каналов делает конвейеры и перенаправление понятными.

К концу урока ты будешь знать, что такое stdin, stdout и stderr, какой номер файлового дескриптора несёт каждый из них, и почему важно держать ошибки в отдельном потоке.

Цель

После этого урока ты сможешь назвать три стандартных потока, указать их номера файловых дескрипторов (0, 1, 2), объяснить назначение каждого и описать, зачем stderr отделён от stdout.

1

Каждый процесс наследует три открытых файловых дескриптора. Когда ядро запускает любой процесс, оно передаёт ему три уже открытых файловых дескриптора — числовых дескриптора, ссылающихся на открытые файлы или устройства. Ядро назначает небольшие целые числа:

  • fd 0 — стандартный ввод (stdin)
  • fd 1 — стандартный вывод (stdout)
  • fd 2 — стандартный поток ошибок (stderr)

Файловый дескриптор — это просто число, которое процесс использует для чтения из чего-либо или записи куда-либо. Ядро хранит таблицу, сопоставляющую каждое число реальному ресурсу (терминальному устройству, файлу, каналу). Три стандартных дескриптора — соглашение, на которое опирается каждая Unix-программа.

2

stdin (fd 0) — это место, откуда процесс читает ввод. По умолчанию stdin подключён к клавиатуре — терминальному устройству. Когда запускается команда, читающая ввод (cat без аргументов, read, grep без имени файла), она блокируется в ожидании твоего ввода. Всё, что ты вводишь, попадает в fd 0.

cat
hello world
hello world
^D

cat без имени файла читает из stdin до конца файла (отправляется нажатием Ctrl+D). Он отражает каждую строку обратно в stdout по мере чтения.

3

stdout (fd 1) — это место, куда процесс пишет обычный вывод. По умолчанию stdout подключён к экрану терминала. Когда ls выводит список файлов или echo печатает строку, эти байты идут в fd 1. Поскольку stdout и stderr по умолчанию подключены к одному экрану терминала, ты обычно не можешь визуально отличить их друг от друга — оба выглядят как текст в терминале.

echo "Hello from stdout"

Вывод:

Hello from stdout

Эта строка пошла в fd 1 → экран терминала.

4

stderr (fd 2) — это место, куда процесс пишет сообщения об ошибках и диагностику. По умолчанию он тоже подключён к экрану терминала, но является отдельным каналом от stdout. Программы пишут ошибки, предупреждения и сообщения о ходе работы в fd 2, чтобы не засорять обычный поток данных на fd 1.

ls /nonexistent

Вывод:

ls: cannot access '/nonexistent': No such file or directory

Это сообщение об ошибке пошло в fd 2, а не в fd 1. Когда оба потока идут на один терминал, ты видишь их вперемежку, но они являются физически отдельными файловыми дескрипторами — а значит, их можно перенаправлять независимо.

Почему это разделение важно? Если скрипт производит 10 000 строк вывода, которые будет обрабатывать другой инструмент, любые сообщения об ошибках, смешанные с fd 1, испортят данные. Хранение ошибок в fd 2 означает, что потребитель fd 1 всегда получает чистый вывод.

5

Оболочка подключает эти потоки к терминалу по умолчанию, но ты можешь перепроложить их. Терминал — лишь цель по умолчанию. Оболочка может направить fd 0 в файл (чтобы программа читала из файла вместо клавиатуры), направить fd 1 в файл (чтобы вывод сохранялся, а не печатался) или подключить fd 1 одного процесса к fd 0 другого (конвейер). Весь этот механизм — перенаправление и каналы — построен поверх этих трёх файловых дескрипторов. Синтаксис ты узнаешь в следующих уроках; пока просто знай, что fd 0, 1 и 2 — это сантехника, которая делает всё это возможным.

Разбор примера

Видим stdout и stderr по отдельности.

Запусти команду, которая одним вызовом производит и обычный вывод, и ошибку:

ls /etc/hosts /nonexistent

Вывод (перемешанный в терминале):

ls: cannot access '/nonexistent': No such file or directory
/etc/hosts

Строка ошибки пришла из fd 2, а список файлов — из fd 1. Они отображаются вместе, потому что оба потока по умолчанию идут на терминал. Теперь перенаправь stdout в файл, чтобы увидеть их раздельно:

ls /etc/hosts /nonexistent > /tmp/out.txt

В терминале видно (только stderr, потому что stdout пошёл в файл):

ls: cannot access '/nonexistent': No such file or directory
cat /tmp/out.txt
/etc/hosts

Два потока всегда были раздельными — терминал просто отображал оба в одном окне.

Почему это работает

На macOS те же три стандартных потока с теми же номерами файловых дескрипторов. Ядро и библиотека C разные, но соглашение POSIX (fd 0/1/2 = stdin/stdout/stderr) идентично. Каждая Unix-производная система его придерживается.

Частая ошибка

Распространённое заблуждение: stderr не «менее важен» — он просто отдельный. Многие программы пишут прогресс-бары, отладочную информацию и подробные логи в stderr, чтобы не засорять stdout. Когда ты перенаправляешь stdout в файл, все эти сообщения всё равно появляются в терминале, потому что идут в stderr. Это намеренный дизайн, а не баг.

Проверь себя
Викторина

Программа записывает сообщение об ошибке. Какой файловый дескриптор она использует по соглашению?

Итог

Каждый процесс стартует с тремя стандартными файловыми дескрипторами: fd 0 (stdin) для чтения ввода, fd 1 (stdout) для записи обычного вывода и fd 2 (stderr) для записи ошибок и диагностических сообщений. По умолчанию все три подключены к терминалу, поэтому оба потока вывода отображаются на экране. Хранение ошибок в отдельном потоке (fd 2) означает, что потребитель fd 1 всегда получает чистые данные — а оболочка может независимо перенаправлять или соединять в конвейер каждый поток. Эта трёхдескрипторная модель — фундамент, на котором работают все перенаправления и конвейеры оболочки.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 5 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.