MyTetra Share
Делитесь знаниями!
Поставь все на конвейер
Время создания: 25.11.2009 21:54
Автор: Журнал "Хакер", номер 73, j1m@list.ru
Текстовые метки: linux, bash, awk, sed, tr, конвейер, поток, строка, обработка
Раздел: Компьютер - Linux - Bash - Команды и скрипты
Запись: xintrea/mytetra_syncro/master/base/0000001317/text.html на raw.github.com

Анализ и преобразование текстовых потоков

*nix славится своими программами-фильтрами. Множество маленьких программок, каждая из которых выполняет только одну определенную функцию, наполняют каталоги /bin и /usr/bin. С помощью таких фильтров, как cut, grep, sed, можно привести к нужному виду практически любой поток текстовой информации. Появившись около тридцати лет назад, они и по сей день активно применяются, что доказывает эффективность конвейерной обработки информации.

Маленькие помощники

Две наиболее часто выполняемые над текстом операции - сортировка и поиск. Начнем с сортировки, а поиск рассмотрим позже. Для сортировки строк применяется команда sort (кто бы мог подумать :), которая читает входной поток и пишет отсортированные строки в выходной поток. У программы есть несколько интересных флагов: -f - игнорировать регистр букв, -d - сортировать только по буквам и цифрам, -n - сортировать по цифрам, -r - обратная сортировка.

Команде можно указать, по какому полю выполнять сортировку. Поля - это последовательности символов, разделенные пробелом или табуляцией. Для этого достаточно указать флаг +номер_поля. Так, можно отсортировать список файлов по размеру:

$ ls -l | sort +5 -n

Для разбиения строк удобно использовать cut. С помощью этой программы можно вырезать из строк отдельные части, которые и будут выведены на экран. Приведем пример:

$ echo UNIXLinuxBSD | cut -c 5-10

На экран будет выведено «Linux». Как это работает? С помощью флага -c мы задали диапазон позиций символов, которые хотим увидеть. Слово «Linux» как раз и занимает позиции с пятой по десятую в строке «UNIXLinuxBSD». Одним из основных достоинств cut является способность работать с полями. Для этого предусмотрено два флага: -d разделитель_полей и -f список_полей. С помощью первого можно указать символ, который будет использоваться в качестве разделителя полей (по умолчанию знак табуляции), а с помощью второго - список выводимых полей. Пример:

$ date

Сбт Ноя 13 17:57:08 GMT+6 2004

$ date | cut -d " " -f 4

17:57:08

Здесь мы указали в качестве разделителя полей знак пробела и вывели на STDOUT четвертое поле.

Довольно интересной и полезной программой является tr(1). Она заменяет символы, указанные в первом аргументе, на соответствующие символы во втором аргументе. Так, команда

$ cat file | tr a e

заменит все буквы «a» на «e». Можно использовать диапазоны:

$ cat file | tr 'a-z' 'A-Z'

и заменить все строчные буквы прописными - это, кстати, наиболее частое применение этой команды. Указав флаг -d, можно удалять символы из текста.

Часто бывает необходимо просто подсчитать количество строк или слов в тексте. Это легко проделать при помощи простенькой программки wc(1). По дефолту она печатает количество строк, слов и символов во входном потоке. С помощью флагов можно указать, что именно необходимо вывести на экран: -c - символы, -l - строки или -w - слова.

Отдельно стоит упомянуть о двух программах: head(1) и tail(1). Первая используется для просмотра первых десяти строк файла, а вторая - последних десяти. Программы очень похожи и управляются идентичными флагами. Так, с помощью флага -n можно изменить количество печатаемых строк, а флаг -c позволяет измерять порцию выводимых данных не в строках, а в байтах. Помимо этих ключей, tail принимает очень полезный (и полюбившийся админам) флаг -f. После запуска с этим флагом программа не закончит свою работу, а продолжит выводить данные по мере их поступления. Для слежения за логами лучшего решения не найти.

Найдется все!

Назначение утилиты grep(1) - поиск в текстовых файлах. Он выдает все строки, содержащие образец поиска. Например команда

$ ls | grep .txt

покажет имена файлов с расширением .txt. В качестве образца поиска можно использовать регулярное выражение. Некоторые полезные флаги: -E - использовать расширенные регулярные выражения (egrep является синонимом grep –E), -i - игнорировать регистр букв, -v - выводить строки, не соответствующие образцу, -n - выводить номера строк, -f - читать образец поиска из файла, --color - подсвечивать совпадения.

Долой интерактивность!

Ты видел редактор без интерфейса и вообще без интерактивного общения с пользователем? Нет? Ты многое потерял, тебе просто необходимо познакомиться с sed(1). Потоковый редактор sed предназначен для редактирования текста на лету. Он читает входной поток, выполняет необходимые преобразования в соответствии с указанными командами и записывает результат в выходной поток. Sed управляется командами, многим из которых может предшествовать адрес (номер строки или регулярное выражение, заключенное в «/») или диапазон адресов (два номера строки, разделенные запятой), последняя строка адресуется с помощью символа «$». Любая команда может быть задана без указания адреса, в этом случае она будет применена ко всем строкам. Чтобы не запутаться, команды, принимающие адреса, я буду предварять знаком «[x]», а команды, принимающие адреса или диапазоны адресов, знаком «[x,x]». Так что же можно сделать с помощью sed?

1. Его удобно использовать для замены слов в тексте с помощью команды [x,x] s/регулярное_выражение/замена/флаги. Например команда

$ cat file.txt | sed 's/UNIX/Linux/g' > file2.txt

заменит все слова UNIX на Linux и запишет результат в file2.txt. Флаг g нужен, если слово UNIX встречается несколько раз в одной строке.

2. Добавлять произвольную строку в поток командой [x]a\. Команда

$ cat file.txt | sed '10a\Здесь был я.'

вставит строку «Здесь был я.» после десятой строки.

3. Заменять произвольные строки командой [x]i\, которая работает подобно команде [x]a\.

4. Удалять строки командой [x,x]d:

$ cat file.txt | sed '10,$d'

$ cat file.txt | sed '/Windows/d'

Первая команда удалит строки с десятой до последней, а вторая - все строки, содержащие слово «Windows».

5. Добавлять содержимое другого файла в поток командой [x]r файл.

6. Использовать вместо tr. Для этого есть команда [x]y/источник/цель/ (эквивалент: tr источник цель).

7. Использовать вместо head:

$ cat file.txt | sed 10q

Команда [x]q приводит к немедленному завершению работы sed. Поэтому в приведенном примере напечатается только десять первых строк, а на десятой строке произойдет выход из редактора.

8. Использовать вместо grep:

$ cat file.txt | sed -n '/образец/p'

По дефолту sed печатает все строки из входного потока в выходной, такое поведение можно изменить, используя флаг -n. Теперь печать строк можно задать только командой [x,x]p. В приведенном выше примере будут выведены только строки, совпадающие с образцом, что эквивалентно команде

$ cat file.txt | grep образец

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

Sed может делать с текстом очень многое, для дальнейшего изучения советую почитать info-страницу.

Найти и обработать

Вот и добрались наши руки до одного из самых мощных и сложных инструментов, предназначенных для редактирования текста - awk. Это язык поиска и обработки шаблонов, по синтаксису он подобен языку C. В нем присутствует даже всеми любимая функция printf. Awk позиционировался авторами как более мощная замена sed, поэтому программы awk похожи на сценарии sed. Программа состоит из пар шаблон-действие, имеющих вид

шаблон { действие }

где шаблон может быть: отношением (<, > , ==, != и т.д.); регулярным выражением, заключенным в «/»; сопоставлением шаблону (~, !~); комбинацией всего перечисленного. Действие - это оператор или блок операторов, разделенных «;». Каждая входная строка сравнивается с шаблоном из каждой пары шаблон-действие. В каждой паре либо шаблон, либо действие может отсутствовать. В случае если не указан шаблон, действие выполняется для каждой входной строки. Если не указано действие, оно назначается по умолчанию - печать строки.

После запуска программы все входные строки разбиваются на поля. Поля помещаются в переменные: $1, $2, $3 и т.д. Вся строка адресуется переменной $0. Пример:

$ date | awk '{ print $4 }'

В этом примере шаблон опущен. С помощью оператора print распечатывается четвертое поле. Без указания аргументов print распечатает всю строку. По дефолту в качестве разделителя полей используется пробел. Указать другой разделитель можно, используя флаг -F. Еще пример:

$ cat /etc/passwd | awk -F: '/boris/ { print $3, $6 }'

Эта команда напечатает uid (третье поле) и домашний каталог (шестое поле) пользователя с ником boris. Помимо $1, $2, $N, в awk имеется еще несколько встроенных переменных, самые важные из которых:

  • FS - разделитель полей (используется вместо -F)
  • OFS - разделитель полей в выходном потоке
  • NF - общее число полей
  • NR - номер текущей строки
  • FILENAME - имя входного файла
  • ARGC - количество аргументов командной строки
  • ARGV - массив аргументов командной строки

Помимо встроенных переменных, присутствуют также встроенные шаблоны: BEGIN и END. Действие шаблона BEGIN выполняется до начала чтения входного потока, а действие шаблона END - после прочтения последней строки потока. Теперь посмотрим, что нам дает использование встроенных переменных и шаблонов, для этого возьмем команду

$ cat /etc/passwd | awk 'BEGIN { FS=":" } /boris/ { print $3, $6 }'

Этот пример аналогичен предыдущему. При введении новых переменных в awk-программу объявлять их не нужно, они будут создаваться при инициализации. Причем переменная сама примет необходимый тип в зависимости от операций, выполняемых над ней:

$ awk 'BEGIN { a = 13 + 17; print a }'

$ awk 'BEGIN { a = "UN" "IX"; print a }'

По окончании выполнения первой команды на экран будет выведено число 30, а вторая команда выведет слово «UNIX». Конкатенация (слияние) строк выполняется операцией «пробел». Арифметические операции такие же, как в языке C. Массивы могут быть ассоциативными (как хэши в perl), то есть запись вида circle[color] = red; воспринимается интерпретатором нормально.

В awk присутствуют все стандартные управляющие операторы, такие как ветвления и циклы. В большинстве своем они повторяют операторы языка C. Вот только не знаю, как часто тебе придется ими пользоваться. При работе с текстом они редко бывают нужны.

Напоследок рассмотрим пример:

$ cat file | awk '

length > 72 {

printf("%s %d %s\n", "Строка", NR, "длинная.");

long++;

next;

}

{

printf("%s %d %s\n", "Строка", NR, "короткая.");

short++;

}

END {

printf("%s %d\n", "Всего длинных строк:", long);

printf("%s %d\n", "Всего коротких строк:", short);

}'

Это маленькая программа подсчитывает количество длинных и коротких строк в файле. Как ты уже, наверное, заметил, она состоит из трех блоков шаблон-действие. В первом блоке выясняется, длиннее ли строка 72-х символов (по умолчанию length вызывается с параметром $0). Если результат положительный, выводится сообщение и увеличивается счетчик (переменная long). Далее следует обратить внимание на оператор next, который принуждает интерпретатор перейти к обработке следующей строки. Таким образом, действие второго блока будет выполнено только в случае невыполнения действия первого блока. Последний блок END будет выполнен в самом конце и выведет общее количество коротких и длинных строк.

Некоторые встроенные функции awk

  • lenght(s) - возвращает длину строки s
  • index(s1, s2) - возвращает позицию вхождения строки s2 в строке s1
  • substr(s, i, n) - возвращает подстроку в n символов подстроки s, начиная с i
  • split(s, arr, sep) - разбивает строку s на основе разделителя sep и помещает результат в массив arr
  • system(c) - выполняет внешнюю команду s и возвращает код возврата
  • printf(format, a1, a2, ...) - выводит данные в соответствии с форматом printf(3)
  • toupper(str) - переводит копию строки str в верхний регистр
  • tolower(str) - переводит копию строки str в нижний регистр
  • cos(x) - возвращает косинус x
  • sin(x) - возвращает синус x
  • sqrt(x) - возвращает квадратный корень x

Регулярные выражения стандарта POSIX

Атомы (может быть любым неспециальным символом):

  • . - любой символ, кроме символа новой строки
  • [...] - любой символ из списка
  • [^...] - любой символ, кроме символов из списка
  • [n-m] - любой символ из диапазона символов
  • ^ - начало строки
  • $ - конец строки
  • \ - экранирует символы специального назначения (^.[$()|*+?{\)
  • (...) - выделяет регулярное выражение в группу

Классы (тоже являются атомами):

  • [:alnum:] - любая буква или цифра
  • [:alpha:] - любая буква
  • [:blank:] - символ пробела или символ табуляции
  • [:cntrl:] - управляющий символ
  • [:digit:] - любая цифра
  • [:graph:] - печатаемый или псевдографический символ
  • [:lower:] - любая буква в нижнем регистре
  • [:print:] - печатаемый символ
  • [:upper:] - любая буква в верхнем регистре
  • [:xdigit:] - любая шестнадцатеричная цифра

За атомами может следовать:

  • * - символ повторяется ноль или больше раз
  • + - символ повторяется один или больше раз
  • ? - символ повторяется ноль или один раз
  • {n,m} - символ повторяется от n до m раз

Атом, за которым следует один из этих символов, называется частью. Часть или несколько частей образуют ветвь. Полное регулярное выражение состоит из одной или более ветвей, разделенных символом «|», который выступает в качестве логического «или».

Заключение

Столь странное название язык awk получил в наследство от своих авторов. AWK - это первые буквы фамилий: Aho, Weinberger и Kernighan.

Потоковый редактор sed был создан на базе редактора ed Л. Мак-Махоном.

Скрипты sed и awk не обязательно вызывать с помощью ключа -f, можно прописать в начало скрипта строку «#!/bin/sed –f» или «#!/bin/awk –f», сделать файл исполняемым и запускать как обычный бинарник.

Так же в этом разделе:
 
MyTetra Share v.0.59
Яндекс индекс цитирования