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