Конвейер
Нередко возникают ситуации, когда нужно обработать вывод одной программы какой-то другой программой. Пользуясь перенаправлением ввода-вывода, можно сохранить вывод одной программы в файле, а потом направить этот файл на ввод другой программе. Однако то же самое можно сделать и более эффективно: перенаправлять вывод можно не только в файл, но и непосредственно на стандартный ввод другой программе. В этом случае вместо двух команд потребуется только одна — программы передают друг другу данные «из рук в руки», в Linux такой способ передачи данных называется конвейер.
В bash
для перенаправления стандартного вывода на стандартный ввод другой программе служит символ «|
». Самый простой и наиболее распространённый случай, когда требуется использовать конвейер, возникает, если вывод программы не умещается на экране монитора и очень быстро «пролетает» перед глазами, так что человек не успевает его прочитать. В этом случае можно направить вывод в программу просмотра (less
), которая позволит не торопясь пролистать весь текст, вернуться к началу и т. п. [
[methody@localhost methody]$ cat cat.info | less
Пример 9. Простейший конвейер
Можно последовательно обработать данные несколькими разными программами, перенаправляя вывод на ввод следующей программе и организуя сколь угодно длинный конвейер для обработки данных. В результате получаются очень длинные командные строки вида «cmd1 | cmd2 | ... | cmdN
», которые могут показаться громоздкими и неудобными, но оказываются очень полезными и эффективными при обработке большого количества информации, как мы увидим далее в этой лекции.
Организация конвейера устроена в shell по той же схеме, что и перенаправление в файл, но с использованием особого объекта системы — канала. Если файл можно представить в виде Коробки с Данными, снабженной Клапаном для Чтения или Клапаном для Записи, то канал — это оба Клапана, прикленные друг к другу вообще без Коробки. Для определённости между Клапанами можно представить Трубу, немедленно доставляющую данные от входа к выходу (английский термин — «pipe» — основан как раз на этом представлении, а в роли Трубы выступает, конечно же, сам Linux). Каналом пользуются сразу два процесса: один пишет туда, другой читает. Связывая две команды конвейером, shell открывает канал (заводится два дескриптора — входной и выходной), подменяет по уже описанному алгоритму стандартный вывод первого процесса на входной дескриптор канала, а стандартный ввод второго процесса — на выходной дескриптор канала. После чего остаётся запустить по команде в этих процессах и стандартный вывод первой попадёт на стандартный ввод второй.
- канал
- Неделимая пара дескрипторов (входной и выходной), связанных друг с другом таким образом, что данные, записанные во входной дескриптор, будут немедленно доступны на чтение с выходного дескриптора.
Фильтры
Если программа и вводит данные, и выводит, то её можно рассматривать как трубу, в которую что-то входит, а что-то выходит. Обычно смысл работы таких программ заключается в том, чтобы определённым образом обработать поступившие данные. В Linux такие программы называют фильтрами: данные проходят через них, причём что-то «застревает» в фильтре и не появляется на выходе, что-то изменяется, что-то проходит сквозь фильтр неизменным. Фильтры в Linux обычно по умолчанию читают данные со стандартного ввода, а выводят на страндартный вывод. Простейшим фильтром Мефодий уже пользовался много раз — это программа cat
: собственно, никакой «фильтрации» данных она не производит, она просто копирует стандартный ввод на стандартный вывод.
Данные, проходящие через фильтр, представляют собой текст: в стандартных потоках ввода-вывода все данные передаются в виде символов, строка за строкой, как и в терминале. Поэтому могут быть состыкованы при помощи конвейера ввод и вывод любых двух программ, поддерживающих стандратные потоки ввода-вывода. Это напоминает стандартный конструктор, где все детали совмещаются между собой.
В любом дистрибутиве Linux присутствует набор стандартных утилит, предназначенных для работы с файловой системой и обработки текстовых данных. Многими из них Мефодий уже успел воспользоваться: это who
, cat
, ls
, pwd
, cp
, chmod
, id
, sort
и др. Мефодий уже успел заметить, что каждая из этих утилит предназначена для исполнения какой-то одной операции над файлами или текстом: вывод списка файлов в каталоге, копирование, сортировка строк, хотя каждая утилита может выполнять свою функцию несколько по-разному, в зависимости от переданных ей ключей и параметров. При этом все они работают со стандартными потоками ввода/вывода, поэтому хорошо приспособлены для построения конвейеров: последовательно выполняя простые операции над потоком данных, можно решать довольно нетривиальные задачи.
Принцип комбинирования элементарных операций для выполнения сложных задач унаследован Linux от операционной системы UNIX (как и многие другие принципы). Подавляющее большинство утилит UNIX, не потеряли своего значения и в Linux. Все они ориентированы на работу с данными в текстовой форме, многие являются фильтрами, все не имеют графического интерфейса и вызываются из командной строки. Этот пакет утилит называется coreutils
.
Структурные единицы текста
Работу в системе Linux почти всегда можно представить как работу с текстами. Поиск файлов и других объектов системы — это получение от системы текста особой структуры — списка имён. Операции над файлами: создание, переименование, перемещение, а также сортировка, перекодировка и прочее, означает замену одних символов и строк на другие либо в каталогах, либо в самих файлах. Настройка системы в Linux сводится непосредственно к работе с текстами — редактированию конфигурационных файлов и написанию сценариев (подробнее об этом см. лекции Возможности командной оболочки и Конфигурационные файлы).
Работая с текстом в Linux, нужно принимать во внимание, что текстовые данные, передаваемые в системе, структурированы. Большинство утилит обрабатывает не непрерывный поток текста, а последовательность единиц. В текстовых данных в Linux выделяются следующие структурные единицы:
- Строки
- Строка — основная единица передачи текста в Linux. Терминал передаёт данные от пользователя системе строками (командная строка), множество утилит вводят и выводят данные построчно, при работе многих утилит одной строке соответствует один объект системы (имя файла, путь и т. п.),
sort
сортирует строки. Строки разделяются символом конца строки «\n
» (newline). - Поля
- В одной строке может упоминаться и больше одного объекта. Если понимать объект как последовательность символов из определённого набора (например, букв), то строку можно рассматривать как состоящую из слов и разделителей.
Например, в командной строке разделителями являются символы пробела и табуляции (см. раздел Terminal..Слова и разделители).
В этом случае текст от начала строки до первого разделителя — это первое поле, от первого разделителя до второго — второе поле и т. д. В качестве разделителя можно рассматривать любой символ, который не может использоваться в объекте. Например, если в пути «/home/methody
» разделителем является символ «/
», то первое поле пусто, второе содержит слово «home
», третье — «methody
». Некоторые утилиты позволяют выбирать из строк отдельные поля (по номеру) и работать со строками как с таблицей: выбирать и объединять нужные колонки и проч. - Символы
- Минимальная единица текста — символ. Символ — это одна буква или другой письменный знак. Стандартные утилиты Linux позволяют заменять одни символы другими (производить транслитерацию), искать и заменять в строках символы и комбинации символов.
Символ конца строки в кодировке ASCII совпадает с управляющей последовательностью «^J
», «перевод строки», однако в других кодировках он может быть иным. Кроме того, на большинстве терминалов — но не на всех! — вслед за переводом строки необходимо выводить ещё символ возврата каретки («^M
»). Это вызвало путаницу: некоторые системы требуют, чтобы в конце текстового файла стояло оба этих символа в определённом порядке. Чтобы путаницы избежать, в UNIX (и, как следствие, в Linux), было принято единственно верное решение: содержимое файла соответствует кодировке, а при выводе на терминал концы строки преобразуются в управляющие последовательности согласно настройке терминала.
В распоряжении пользователя Linux есть ряд утилит, выполняющих элементарные операции с единицами текста: поиск, замену, разделение и объединение строк, полей, символов. Эти утилиты, как правило, имеют одинаковое представление о том, как определяются единицы текста: что такое строка, какие символы являются разделителями и т. п. Во многих случаях их представления можно изменять при помощи настроек. Поэтому такие утилиты легко взаимодействуют друг с другом. Комбинируя их, можно автоматизировать довольно сложные операции по обработке текста.