Потоки
В каждом языке есть свой способ напечатать результат на экран.
Javascript:
PHP:
Python:
Java:
Ruby:
Несмотря на разнообразие языков и способов печати, с точки зрения операционной системы все программы работают абсолютно идентично. При старте любой программы операционная система связывает с ней три потока:
STDIN (Standard Input)
STDOUT (Standard Output)
STDERR (Standard Error)
Для языка программирования эти потоки выглядят как файлы, и взаимодействие с ними происходит как с файлами. В этом уроке мы изучим, как работают потоки на базовом уровне.
Начнем с потока STDOUT. Он отвечает за вывод на экран. Во время каждой печати на экран в программе на любом языке происходит следующие два шага:
Функция печати записывает данные в поток STDOUT с помощью функции
write
Операционная система решает, куда вывести результат. По умолчанию вывод происходит на экран терминала
Чтобы хорошо понять эту тему, нужно подробно изучить устройство операционных систем, в частности подсистемы, отвечающей за процессы и файловую систему. Но мы попробуем кратко рассказать самое важное для понимания.
В двух словах, языки программирования не взаимодействуют с монитором и железом в целом. За взаимодействие с железом целиком и полностью отвечает операционная система. Программы могут только лишь попросить операционную систему выполнить ту или иную задачу.
При таком разделении реализация языков программирования сильно упрощается. Достаточно знать про существование STDOUT и уметь писать в этот поток, а дальше все сделает операционная система.
Именно благодаря такому разделению и потокам, можно написать программу на одном компьютере и без проблем запустить ее на другом компьютере с другой конфигурацией и монитором.
Самое удивительное начинается дальше. Операционные системы позволяют подменять потоки при старте системы, что открывает интересные возможности.
Например, вывод любой команды в bash можно не выводить на экран, а записать в файл:
Запустив эту команду, вы не увидите на экране ничего нового. Зато в текущей директории появится файл output:
Операция, которую мы сделали выше, называется перенаправление потоков.
Символ >
означает, что нужно взять вывод из команды слева и отправить его в файл, указанный справа. Этот символ >
всегда перезаписывает файл.
Такое перенаправление работает с абсолютно любой командой, которая выводит результаты своей работы в консоль:
Если нужно не перезаписывать, а добавлять, то используйте >>
.
Для экспериментов с выводом удобно использовать встроенную команду echo
. Она принимает на вход строчку и выдает ее в STDOUT, который уже можно перенаправлять:
Кроме стандартного вывода, с каждым процессом ассоциируются два дополнительных потока:
STDERR — вывод ошибок
STDIN — стандартный ввод
STDIN работает в обратную сторону: через него программа может получать данные на вход.
В *nix-системах встроена утилита wc
(сокращение от word count). Она умеет считать количество слов, строк или символов в файле. Когда мы говорим о файле, то в *nix-системах это почти всегда означает, что данные можно передать и в стандартный поток ввода:
Выглядит довольно логично — стрелка меняет свое направление в другую сторону и содержимое файла отправляется в поток STDIN запускаемой программы wc
.
Теперь объединим перенаправление ввода и вывода:
Кстати, таким же способом можно отправить вывод на печать, но мы оставим эту возможность на самостоятельное изучение.
Последний вопрос связан с тем, зачем нужен поток STDERR.
Поток STDERR позволяет отделить нормальный вывод программы от возникающих ошибок.
Как и STDOUT, он по умолчанию выводит информацию на экран. Такой подход удобен при ведении логов, для реагирования и отладки.
Будьте осторожны, потому что перенаправление вывода в файл перенаправляет только STDOUT. Убедиться в этом очень просто.
Если попробовать отобразить содержимое несуществующей директории, то команда ls
выдаст ошибку:
Теперь попробуем перенаправить вывод в файл output:
Перенаправление есть, но сообщение вывелось на экран. Это произошло именно по той причине, что STDERR остался привязан к экрану, а внутри файла output — пустота. Решить эту задачу можно несколькими способами.
Перенаправление STDERR в STDOUT
В unix-системах за каждым потоком закреплен определенный номер, который называется файловым дескриптором. С помощью него можно получить доступ к потокам ввода и вывода.
Существуют следующие стандартные потоки ввода и вывода:
STDIN — 0
STDOUT — 1
STDERR — 2
Первый способ — перенаправить STDERR в STDOUT или отправить оба потока в файл.
Часто стандартный поток ошибок объединяют со стандартным потоком вывода, чтобы можно было обрабатывать ошибки и результат выполнения вместе:
В примере выше обратите внимание на &
после символа перенаправления >
. По правилам синтаксиса символ &
нужно ставить, чтобы указать поток, в который осуществляется перенаправление.
В примере ниже 2>&1
написано перед > output
. Это не будет работать, потому что когда интерпретатор прочитает 2>&1
, он еще не будет знать, куда перенаправлен стандартный поток вывода. Поэтому потоки ошибок и вывода не будут объединены:
Перенаправление потока STDERR бывает полезно само по себе, без вывода в файл:
Перенаправление конкретного потока в файл
Чтобы перенаправить конкретный поток, нужно указать его номер перед >
:
Перенаправление обоих потоков в файл
Этот вариант используется чаще всего. Он помогает проводить отладку и подсказывает, почему вообще возникла та или иная ошибка:
Дополнительные материалы
Вопросы для самопроверки
За что отвечает поток STDIN?
Вывод ошибок
Вывод данных на экран
Прием входящих данных в процесс
Какой оператор перенаправляет STDOUT в файл?
>
|
Перевод строки
<
Какой оператор дописывает данные в конец файла?
<
>
>>