8. Стандартные потоки#

Введение#

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

Почему с программой связаны два исходящих потока? В стандартный поток вывода передаются результаты работы программы, тогда как в стандартный поток ошибок – диагностические сообщения, предупреждения, информирование о текущем состоянии процесса. Если результат программы из стандартного потока вывода может быть использована другой программой, то сообщения из потока ошибок в первую очередь направлены пользователю.

До этого момента мы употребляли выражение, что команда “выводит на экран терминала”. Фактически, команда отправляет данные в стандартный поток вывода, который подключен к терминалу. Таким же успехом данные могут быть перенаправлены в файл или другую команду. Это дает неожиданные применения примитивным на первый взгляд командам. Рассмотрим это на примере команд echo, read и cat.

Любой язык программирования содержит функцию для вывода строки на экран. В интерпретаторе bash для этого существует команда echo. Она отправляет данные, переданные ей через аргументы командной строки, в стандартный вывод. Так как стандартный вывод по умолчанию подключен к терминалу, то команда выведет свои аргументы на экран. Перевод строки добавляется автоматически к выводимым данным. Опция -n отменяет такое поведение. Утилиту echo часто используют в сценариях, где она печатает диагностические сообщения и значения переменных окружения. Дополнительно, она умеет разворачивать регулярные выражения до имен файлов:

[sppo@machine ~]$ echo main.*
main.c main.cc

Если можно выводить данные, то должна существовать обратная операция ввода данных от пользователя. Команда read считывает одну строку из стандартного ввода и помещает слова из строки в переменные, имена которых переданы через аргументы. Если переменных недостаточно для вмещения всех слов, то в последнюю из них поместится остаток необработанной строки.

[sppo@machine ~]$ read var1 var2 var3
Hello terminal! This is user.
[sppo@machine ~]$ echo $var1
Hello
[sppo@machine ~]$ echo $var2
Terminal!
[sppo@machine ~]$ echo $var3
This is user.

Команда cat отправляет в стандартный поток вывода содержимое файлов, имена которых переданы через аргументы командной строки. Она позволяет быстро просматривать небольшие файлы, вмещаемые на экране.

[sppo@machine ~]$ cat main.c
int main(int argc, char** argv)
{
    return 0;
}

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

По умолчанию, два выходных потока привязаны к экрану, а входной поток к клавиатуре терминала. Эту связь можно изменить, привязав потоки к файлу. Такое действие называют перенаправлением потоков. При перенаправлении потока ввода данные будут считаны из файла, а при перенаправлении потока вывода – будут записаны в файл.

Для перенаправления вывода в файл используют символ > и конструкцию вида command > file. В случае с командой echo, запись строки hello terminal в файл my.txt выглядит как echo "hello terminal" > my.txt. Если такого файла не существует, то он будет создан. В противном случае его предыдущее содержимое заменится новым. Чтобы добавить строку без затирания существующего содержимого используют символы >> и конструкцию command >> file. Благодаря перенаправлению стандартного потока вывода в файл echo превращается в текстовый редактор для записи и добавления в файл строки.

Команда read считывает данные из стандартного потока ввода. Для перенаправления входящего потока командная оболочка bash предлагает символ < и конструкцию вида command < file. Команда read var1 var2 var3 < file.txt считывает из файла file.txt первую строку и размещает ее в переменных окружения var1, var2 и var3.

Утилита cat совместно с перенаправлением потоков превращается в инструмент:

  • объединения файлов: cat part-1.txt part-2.txt part-3.txt > file.txt.

  • создания многострочных файлов: cat > file.txt.

Перенаправление потоков ввода и вывода можно скомбинировать в одной команде. Так, команда cat < part-4.txt >> file.txt добавит в конец файла file.txt содержимое part-4.txt.

До этого момента перенаправление происходило только стандартных потоков ввода и вывода, но не ошибок. Хотя он и связана с терминалом, как и поток вывода, но он перенаправляется отдельной инструкцией. Чтобы изучить перенаправление ошибок, познакомимся с утилитой find для поиска файлов. Команда find ~ -name "file.txt" ищет файл по имени file.txt в домашнем каталоге. Если по ходу поиска не получилось зайти в каталог, то это будет указано в сообщении, отправляемом в стандартный поток ошибок. Чтобы отключить эти сообщения, перенаправим поток ошибок в файл конструкцией вида command 2> file: find / -name "file.txt" 2> out.txt.

Отправляя сообщение об ошибке в файл, мы нагружаем ФС и расходуем дисковое пространство, сохраняя бесполезную информацию. Когда хотят отключить исходящий поток, его перенаправляют в специальный файл /dev/null, называемой системной корзиной. Направленные в нее данные бесследно исчезают. В случае с примером выше, измененная команда выглядит как: find / -name "file.txt" 2> /dev/null.

Перенаправление стандартному потоку вывода в системную корзину применяют, когда пользовтелю интересует не результат команды, а ошибки в ходе выполнения. Классическим примером такого явления относится использование утилиты make для сборки программ в среде UNIX. Утилита указывает ход процесса сборки сообщениями в стандартный поток вывода, а ошибки от компилятора и компоновщика отправляет в стандартный поток ошибок. Сообщения об ошибках теряются среди остальных. Команда make > /dev/null перенаправляет стандартный вывод в системную корзиную и оставляет на экране только сообщения об ошибке.

Чтобы отключить оба исходящих потока программы, их перенаправляют в системную корзину командой make > /dev/null 2>&1. Здесь указано, что поток вывода перенаправляется в системную корзину, а поток ошибок туда же, куда был перенаправлен поток вывода до этого. Здесь важна последовательность. Если поменять местами переправляющие части, то нужный эффект не достигается. Более упрощенные записи command >& file или command &> file также позволяет достичь этого, но они не соответствуют стандарту POSIX.

До этого момента нам известны два способа отправки данных в поток ввода – из клавиатуры и файла. Файл освобождает пользователя от рутины каждый раз вбивать данные с клавиатуры. Связка файла с данными и сценария, где эти данные употребляются, в ходе переноса может разрушиться. Самодостаточные сценарии данные добывают самостоятельно или хранят у себя в виде строки. Для отправки в поток ввода строки применяют команду command <<< "string". Многострочный текст в поток ввода передают посредством символов << и следующих за ним разделителя:

command << STOP
$ Hello!
$ This is text
$ for command utility
$ STOP

Этот прием называется “document here” и применим в сценариях и командной строке. Разделителем может быть любое слово, а не только STOP. С помощью него в сценарии можно представить многострочный файл.

Конвейер команд#

Перенаправление потоков позволяет нам сохранить результат выполнения команды в файл, чтобы воспользоваться им позднее, например передав его в другую команду. Здесь файл играет роль промежуточного буфера. Но можно отказаться от него, если исходящий поток первой команды соединить с входящим потоком второй. Эту цепочку можно продолжить дальшее, добавив другие команды. В командной оболочке bash потоки двух команд объединяются посредством символа |: command1 | command2. Получившуюся конструкцию называют конвейером.

Один пользователь может войти в систему несколько раз из разных терминалов. Сформирует отчет, в котором будут указаны имя пользователя и количество входов. Утилита who выводит таблицу с информацией о зашедших в машину пользователей. В первом столбце стоит имя пользователя, затем идут последовательно имя терминала, дата и время входа и IP-адрес удаленной машины. Из каждой строки таблицы вычленим первый столбец, имя пользователя, командой awk '{print $1}'. Получившийся список имен отсортируем командой sort и исключим повторяющиеся имена, подсчитав повторения командой uniq --count. В результате получим следующую составную команду или конвейер:

who | awk {print $1} | sort | uniq --count

Установка программ из менеджера пакетов apt-get требует подтверждение действий вводом символа y. Существует команда yes, единственная задача которой соглашаться, отправив символ y в стандартный поток вывода. Объединим ее с командой установки компилятора gcc:

yes | apt-get install gcc

Note

Если вывод команды объемный и не обозревается на одном экране, то для следует вывод перехватить утилитой less и пролистывать ее командами. Пример такого конвейера: ls -l /usr/bin | less.

При составлении конвейера новичку следует учитывать, что не все команды принимают данные из потока и выдают данные в поток. Существуют команды, которые:

  • принимают данные из потока и выдают результат в поток (sort);

  • принимают данные из потока, но не выдают результат (read);

  • не принимают данных из потока, но выдают результат (ls, echo);

  • не принимают данные из потока и не выдают результат (cd, rm, mv).

Графически, это выглядит как показано ниже:

../_images/command-in-out.png

Некоторые команды могут как принимать данные из стандартного потока, так и брать их из файла, в зависимости от опций и аргументов. Так, sort отсортирует данные из файла, если его имя передано через свободный аргумент: sort file.txt. Если ей передать имя файла через опцию --output, то выходные данные сохранятся в этот файл: sort --output==out.txt file.txt. В последнем примере команды sort не будет обращаться ни к потоку ввода, ни к потоку вывода.

Дополнительные источники#

  1. Redirections (Bash Reference Manual)

Вопросы для самоконтроля#

  1. Какими стандартными потоками обладает консольная программа?

  2. Куда можно перенаправить данные из стандартных потоков?

  3. Чем отличаются данные из стандартных потоков вывода и ошибок?

  4. Что делают команды echo, read и cat? Приведите примеры их использования (без перенаправления потоков).

  5. Что такое системная корзина и как перенаправить в нее потоки?

  6. Что такое конвейер?

Упражнения#

  1. Создайте файл main.c со строкой int main(int argc, char** argv). В этот же файл добавьте дополнительно строку { return 0; }. Проверьте, получилось ли у вас сделать это.

  2. Считайте слова из файла message.txt (с одной строкой Hello terminal!). Проконтролируйте, что у вас получилось это сделать.

  3. Создайте и объедините два файла в один. Создайте третий файл и добавьте его в конец предыдущего файла.

  4. Покажите пример перенаправления в файл потока ошибок.

  5. Команда wc подсчитывает байты, слова и строки. Напишите команду, которая выведет на экран количество файлов и каталогов в каталоге /usr/bin.

  6. Команда sort сортирует строки. Если ей на вход не передан файл, то данные для сортировки берет из стандартного потока ввода. Каждый сортируемый элемент должен начинаться с новой строки. Команда echo -e выведет текст с новой строки, если перед ним стоят управляющий символ \n. Опция -e интерпретирует обратный слеш и следующий за ним символ как управляющий символ. Воспользуйтесь этими командами, чтобы отсортировать слова “Полина”, “Кристина”, “Динара”, “Артем”, “Елена”, “Алина”, “Никита”, “Егор”, “Денис”, “Андрей”.

  7. Команда ps -e выведет список всех процессов в системе. Команда grep string выведет в стандартный поток вывода только те строки из стандартного потока ввода, в которых содержится слово “string”. Команда awk '{print $4}' выведет только четвертое слово. Воспользуйтесь этими командами, чтобы вывести на экран идентификаторы тех процессов, в названиях которых есть слово “kworker”.

  8. Подсчитайте количество процессов в системе.

  9. Отсортируйте файлы из /usr/bin по размеру утилитой sort. Для этого обратите внимание на ее опции -n и -k.

  10. Команда xclip взаимодействует с буфером обмена. Передайте с помощью него содержимое файла в графическое приложение.

  11. Командой xclip сохраните в файл скопированный из веб-браузера текст.