В каждом языке есть собственный способ напечатать результат на экран:
javascript
console.log('hello!');
php
<?php
echo 'hello!';
python
print('hello!')
java
System.out.print("hello!");
ruby
puts 'hello!'
Несмотря на разнообразие языков и способов печати, с точки зрения операционной системы, которая запускает программу, все они работают абсолютно идентично. При старте любой программы операционная система связывает с ней три так называемых потока: STDIN (Standard Input), STDOUT (Standard Output) и STDERR (Standard Error). Для языка программирования они выглядят как файлы, и взаимодействие с ними происходит как с файлами. STDOUT как раз отвечает за вывод на экран. Каждый раз, когда в программе (на любом языке) происходит печать на экран, функция печати, на самом деле, записывает с помощью функции write
данные в STDOUT, а вот уже операционная система решает куда вывести результат. По умолчанию вывод происходит на экран терминала.
Здесь нужно сказать, что хорошее понимание этой темы требует знания устройства операционных систем, в частности подсистемы, отвечающей за процессы и файловую систему. В двух словах, никакой язык программирования не может знать про существование экрана, а уж тем более не может с ним взаимодействовать. Ответственность за взаимодействие с железом целиком и полностью лежит на плечах операционной системы, а программы могут только лишь попросить операционную систему выполнить ту или иную задачу. При таком разделении реализация языков программирования сильно упрощается. Достаточно знать про существование STDOUT и уметь писать в него, а дальше всё сделает операционная система. Это значит, что программа, написанная на одном компьютере, без проблем запустится на другом с другой конфигурацией и монитором (или даже без монитора).
Самое удивительное начинается дальше. ОС позволяет подменять эти потоки при старте системы, что открывает интересные возможности. Например, вывод любой команды, запущенной в bash, можно записать в файл вместо вывода на экран.
$ ls -la > output
Запустив эту команду, вы увидите, что на экране ничего не отобразилось, но в текущей директории появился файл output
.
$ cat output
total 44
drwxr-xr-x 5 kirill.m kirill.m 4096 Aug 29 09:39 .
drwxr-xr-x 8 root root 4096 Apr 26 10:38 ..
-rw------- 1 kirill.m kirill.m 1822 Aug 29 08:45 .bash_history
-rw-r--r-- 1 kirill.m kirill.m 220 Aug 31 2015 .bash_logout
-rw-r--r-- 1 kirill.m kirill.m 3771 Aug 31 2015 .bashrc
drwx------ 2 kirill.m kirill.m 4096 Mar 30 18:10 .cache
-rw------- 1 kirill.m kirill.m 55 Aug 28 18:49 .lesshst
drwxrwxr-x 2 kirill.m kirill.m 4096 Aug 29 08:35 .nano
-rw-rw-r-- 1 kirill.m kirill.m 0 Aug 29 09:39 output
-rw-r--r-- 1 kirill.m kirill.m 655 May 16 2017 .profile
drwx------ 2 kirill.m kirill.m 4096 Jan 22 2018 .ssh
-rw------- 1 kirill.m kirill.m 513 Aug 29 08:06 .viminfo
Операция, которую мы сделали выше, называется перенаправление потоков. Символ >
означает, что нужно взять вывод из команды, указанной слева, и отправить его в файл, указанный справа. >
всегда перезаписывает файл. Такое перенаправление работает с абсолютно любой командой, которая выводит результаты своей работы на экран.
$ grep alias .bash_profile > result
$ cat result
alias fixssh='eval $(tmux showenv -s SSH_AUTH_SOCK)'
Если нужно не перезаписывать, а добавлять, то используйте >>
.
Для экспериментов с выводом удобно использовать встроенную в шелл команду echo
. Она принимает на вход строчку и выдаёт её в STDOUT, который уже можно перенаправлять.
# > перетирает файл
$ echo 'hello' > result
$ cat result
hello
$ echo 'hello' > result
$ cat result
hello
# >> добавляет содержимое в конец файла
$ echo 'hello' >> result
$ cat result
hello
hello
$
Кроме стандартного вывода, с каждым процессом ассоциируются два дополнительных потока: один STDIN (стандартный ввод) и STDERR (вывод ошибок). STDIN работает в обратную сторону: через него программа может получать данные на вход. В *nix-системах встроена утилита wc
(word count — "количество слов"), которая умеет считать количество слов, строк или символов в файле. Когда мы говорим о файле, то в *nix это почти всегда означает, что данные можно передать и в стандартный поток ввода.
# Флаг l (l а не 1) говорит о том, что надо считать количество строк
$ wc -l < result
2
Выглядит довольно логично — стрелка меняет своё направление в другую сторону и содержимое файла отправляется в STDIN запускаемой программы wc
. Теперь сделаем финт и объединим перенаправление ввода и вывода.
$ wc -l < result > output
$ cat output
$ 2
Кстати, таким же способом можно отправить вывод на печать, но оставлю эту возможность на самостоятельное изучение.
Последний вопрос связан с тем, зачем нужен поток STDERR. Он, как и STDOUT, по умолчанию идёт на экран. STDERR позволяет отделить нормальный вывод программы от возникающих ошибок. Такой подход удобен при ведении логов, для реагирования и отладки. Будьте осторожны, перенаправление вывода в файл перенаправляет только STDOUT. Убедиться в этом очень просто. Если попробовать перейти в несуществующую директорию, то команда cd
выдаст ошибку:
$ cd lala
-bash: cd: lala: No such file or directory
Теперь попробуем перенаправить вывод в файл output
$ cd lala > output
-bash: cd: lala: No such file or directory
Перенаправление есть, но сообщение вывелось на экран. Это произошло именно по той причине, что STDERR остался привязан к экрану, а внутри файла output
— пустота. Решить эту задачу можно несколькими способами. Например, перенаправив STDERR в STDOUT, либо отправив их оба в файл.
Перенаправление STDERR в STDOUT
# Сначала STDERR перенаправляется в STDOUT, затем STDOUT в файл
$ cd lala > output 2>&1
$ cat output
-bash: cd: lala: No such file or directory
2
в данном случае обозначает номер потока. В POSIX за каждым потоком закреплён определённый номер, который является файловым дескриптором, если быть точным: STDIN — 0, STDOUT — 1, STDERR — 2. Конструкцию 2>&1
нужно просто запомнить, она говорит о том, что поток с номером 2 отправляем в поток с номером 1.
Перенаправление STDERR бывает полезно само по себе, без вывода в файл.
# STDERR просто перенаправляется в другой поток (STDOUT)
$ cd lala 2>&1
-bash: cd: lala: No such file or directory
Перенаправление конкретного потока в файл
Чтобы перенаправить конкретный поток, нужно указать его номер перед >
.
# Таким образом можно сразу перенаправить STDERR в файл
$ cd lala 2> output
$ cat output
-bash: cd: lala: No such file or directory
Перенаправление обоих потоков в файл
Этот вариант используется наиболее часто, так как помогает легче проводить отладку и понимать почему вообще возникла та или иная ошибка.
# Оба потока, STDERR и STDOUT, перенаправляются в файл
$ cd lala &> output
$ cat output
-bash: cd: lala: No such file or directory
Вам ответят команда поддержки Хекслета или другие студенты.
Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.
Загляните в раздел «Обсуждение»:
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.
Наши выпускники работают в компаниях:
Зарегистрируйтесь или войдите в свой аккаунт