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).
Графически, это выглядит как показано ниже:
Некоторые команды могут как принимать данные из стандартного потока, так и брать их из файла, в зависимости от опций и аргументов.
Так, sort отсортирует данные из файла, если его имя передано через свободный аргумент: sort file.txt.
Если ей передать имя файла через опцию --output, то выходные данные сохранятся в этот файл: sort --output==out.txt file.txt.
В последнем примере команды sort не будет обращаться ни к потоку ввода, ни к потоку вывода.
Дополнительные источники#
Вопросы для самоконтроля#
Какими стандартными потоками обладает консольная программа?
Куда можно перенаправить данные из стандартных потоков?
Чем отличаются данные из стандартных потоков вывода и ошибок?
Что делают команды
echo,readиcat? Приведите примеры их использования (без перенаправления потоков).Что такое системная корзина и как перенаправить в нее потоки?
Что такое конвейер?
Упражнения#
Создайте файл
main.cсо строкойint main(int argc, char** argv). В этот же файл добавьте дополнительно строку{ return 0; }. Проверьте, получилось ли у вас сделать это.Считайте слова из файла
message.txt(с одной строкойHello terminal!). Проконтролируйте, что у вас получилось это сделать.Создайте и объедините два файла в один. Создайте третий файл и добавьте его в конец предыдущего файла.
Покажите пример перенаправления в файл потока ошибок.
Команда
wcподсчитывает байты, слова и строки. Напишите команду, которая выведет на экран количество файлов и каталогов в каталоге/usr/bin.Команда
sortсортирует строки. Если ей на вход не передан файл, то данные для сортировки берет из стандартного потока ввода. Каждый сортируемый элемент должен начинаться с новой строки. Командаecho -eвыведет текст с новой строки, если перед ним стоят управляющий символ\n. Опция-eинтерпретирует обратный слеш и следующий за ним символ как управляющий символ. Воспользуйтесь этими командами, чтобы отсортировать слова “Полина”, “Кристина”, “Динара”, “Артем”, “Елена”, “Алина”, “Никита”, “Егор”, “Денис”, “Андрей”.Команда
ps -eвыведет список всех процессов в системе. Командаgrep stringвыведет в стандартный поток вывода только те строки из стандартного потока ввода, в которых содержится слово “string”. Командаawk '{print $4}'выведет только четвертое слово. Воспользуйтесь этими командами, чтобы вывести на экран идентификаторы тех процессов, в названиях которых есть слово “kworker”.Подсчитайте количество процессов в системе.
Отсортируйте файлы из
/usr/binпо размеру утилитойsort. Для этого обратите внимание на ее опции-nи-k.Команда
xclipвзаимодействует с буфером обмена. Передайте с помощью него содержимое файла в графическое приложение.Командой
xclipсохраните в файл скопированный из веб-браузера текст.