Текст книги "Linux и UNIX: программирование в shell. Руководство разработчика"
Автор книги: Дэвид Тейнсли
сообщить о нарушении
Текущая страница: 7 (всего у книги 25 страниц)
Таблица 9.3. Встроенные переменные awk
Переменная
Что содержит
ARGC
Количество аргументов в командной строке (поддерживается только: в nawk и gawk)
ARGV
Массив аргументов командной строки (поддерживается только в nawk и gawk)
ENVIRON
Массив переменных среды (поддерживается только в nawk и gawk)
FILENAME
Имя файла, обрабатываемого в текущий момент
FNR
Количество уже обработанных записей в текущем файле (поддерживается только в nawk и gawk)
FS
Разделитель полей во входном потоке (по умолчанию пробел); аналогична опции -F командной строки
NF
Количество полей в текущей записи
NR
Количество обработанных записей во входном потоке
OFS
Разделитель полей в выходном потоке (по умолчанию пробел)
ORS
Разделитель записей в выходном потоке (по умолчанию символ новой строки)
RS
Разделитель записей во входном потоке (по умолчанию символ
новой строки)
Переменная ARGC хранит число аргументов командной строки, переданной сценарию awk (точнее, nawk или gawk, т. к. эта переменная появилась только в новых версиях утилиты). Переменная argv хранит значения аргументов командной строки. Доступ к нужному аргументу осуществляется с помощью ссылки ARGV[n], где п – порядковый номер аргумента в командной строке.
Переменная environ хранит значения всех текущих переменных среды. Чтобы получить доступ к нужной переменной, следует указать ее имя, например:
ENVIRON["EDITOR"]=="vi"
Поскольку сценарий awk может обрабатывать большое количество файлов, предусмотрена переменная FILENAME, которая указывает на то, какой файл просматривается в текущий момент.
Переменная fnr хранит номер записи, которую утилита awk обрабатывает в текущий момент; ее значение меньше или равно значению переменной NR, которая отслеживает общее число обработанных записей входного потока. Если сценарий получает доступ более чем к одному файлу, переменная FNR сбрасывается в ноль при открытии каждого нового файла. В переменную NF записывается количество полей текущей записи. Ее значение сбрасывается по достижении конца записи.
Переменная FS содержит символ, используемый в качестве разделителя полей входного потока. Эту переменную можно установить из командной строки с помощью опиии -F. По умолчанию разделителем полей служит пробел. Переменная ofs содержит символ, являющийся разделителем полей в выходном потоке. По умолчанию это тоже пробел.
В переменной ORS хранится разделитель записей в выходном потоке. По умолчанию им является символ новой строки (n). Переменная RS содержит разделитель записей во входном потоке (в большинстве случаев это тоже символ n).
Переменные NF, NR и FILENAME
Представленная ниже команда позволяет быстро определить число записей во входном файле grade.txt. Значение переменной NR отображается по завершении обработки файла.
$ awk 'END {print NR}' grade.txt
В следующем примере на экран выводятся все записи исходного файла. Каждой из них предшествуют два числа: количество полей в записи (переменная NF) и номер записи в файле (переменная nr). В самом конце отображается имя входного файла (переменная FILENAME).
$ awk '{print NF, NR, $0} END {print FILENAME}' grade.txt
1
M. Tansley
05/99
48311
Green
8
40
44
2
J. Lulu
06/99
48317
green
9
24
26
3
P. Bunny
02/99
48
Yellow
12
35
28
4
J. Troll
07/99
4842
Brown-3
12
26
26
5
L. Tansley
05/99,
4712
Brown-2
12
30
28
grade.txt
Переменную NF удобно использовать, когда требуется извлечь из путевого имени последнюю часть, т. е. имя файла или каталога. В этом случае необходимо указать, что разделителем полей является символ '/' и использовать ссылку $NF, которая является обозначением последнего поля текущей записи. Например:
$ pwd
/usr/local/etc
$ echo $PWD | awk -F/ '{print $NF}'
Переменная среды $PWD хранит путевое имя текущего каталога.
9.2.10. Встроенные функции работы со строками
Утилита awk располагает набором универсальных функций преобразования строк. В табл. 9.4 перечислены основные из них.
Таблица 9.4. Функции работы со строками
Функция
Назначение
gsub(r,.s)
Выполняет глобальную замену каждой строки, соответствующей регулярному выражению г, строкой s в пределах текущей записи; появилась в nawk
index(s, t)
Возвращает позицию первого вхождения подстроки t в строку s
length(s)
Возвращает длину строки s
match(s, r)
Проверяет, содержит ли строка s подстроку, соответствующую, регулярному выражению r; появилась в nawk
split(s, a,fs)
Разбивает строку s на элементы, разделенные символом fs, и помещает полученные элементы в массив а
sub(r, s)
Выполняет замену самой первой строки, соответствующей регулярному выражению r, строкой s в пределах текущей записи; появилась в nawk
substr(s, p[,n])
Возвращает подстроку строки s, начинающуюся с позиции p и имеющую длину n; если аргумент п не задан, концом подстроки считается символ (признак конца строки)
Функция gsub()
Благодаря функции gsub() вы сможете выполнить в текущей записи глобальную замену строк, соответствующих заданному регулярному выражению. Например, для изменения номера ученика с 4842 на 4899 введите такую команду:
$ awk 'gsub(4842,4899) {print $0}' grade.txt
J. Troll 07/99 4899 Brown-3 12 26 26
Функция index()
Чтобы узнать позицию первого вхождения подстроки t в строку s, воспользуйтесь функцией index (), только не забудьте взять ее аргументы в двойные кавычки, иначе они будут восприниматься как имена переменных среды. Например, следующая команда возвращает число, определяющее позицию подстроки "ny" в строке "Bunny":
$ awk 'BEGIN {print index("Bunny","ny")}' grade.txt
4
Функция length()
Функция length() возвращает длину переданного ей текстового аргумента. В показанном ниже примере производится поиск информации об ученике с номером 4842, а затем определяется длина имени ученика:
$ awk '$3=4842 {print length($1)" "$1}' grade.txt
7 J.Troll
Следующая команда демонстрирует применение утилиты awk для вычисления длины текстовой строки:
$ awk 'BEGIN {print length("A FEW GOOD MEN")}'
14
Функция match()
Функция match() позволяет проверить, содержит ли строка заданную подстроку. Последняя может быть представлена как литералом в двойных кавычках, так и регулярным выражением. Если поиск прошел успешно, возвращается число, определяющее позицию, с которой начинается вхождение подстроки в искомую строку. В случае неудачи возвращается ноль. Следующая команда проверяет, содержит ли имя ученика с номером 48317 символ 'u':
$ awk '$3=48317 {print match ($1, "u"), $1} ' grade.txt
4 J. Lulu
Функция split()
Функция split() преобразует переданную ей строку в массив и возвращает число элементов в полученном массиве. В следующем примере заданная строка разбивается на три элемента, которые помещаются в массив myarray. Разделителем элементов в данном случае является символ '#'.
$ awk 'BEGIN {print split("123#456#678", myarray,"#"))'
3
Массив myarray будет иметь такую структуру:
mуarray[1]="123" myarray[2]="4 56"
myarray[3]="678"
Функция sub()
Функция sub() применяется для поиска строки, соответствующей заданному шаблону, и ее замены при первом появлении. В этом состоит отличие данной функции от функции gsub(), которая находит все случаи вхождения подстроки в строку, производя соответствующее число замен. Приведенная ниже команда находит запись ученика J. Troll и меняет его рейтинг с 26 на 29 (поле 6), при этом значение поля 7 (тоже 26) остается неизменным:
$ awk '$1=="J. Troll" {sub(26,29,$0) )' grade.txt
J. Troll 07/99 4842 Brown-3 12 29 26
Функция substr()
Функция substr() возвращает указанную часть строки. Вам нужно задать позицию, с которой начинается вхождение подстроки в искомую строку, и длину подстроки. Рассмотрим пример:
$ awk '$1=="L. Tansley" {print substr($1,1,5))' grade.txt
L. Tan
Эта команда возвращает из строки "L. Tansley" подстроку, начинающуюся с первого символа и занимающую пять символов.
Если значение третьего аргумента значительно превышает реальную длину строки, функция substr () возвращает все символы строки, начиная с указанной позиции:
$ awk '$1=="L. Tansley" {print substr ($1,3, 99) ) ' grade.txt
Tansley
То же самое происходит, когда третий аргумент вообще не указан. Например, следующая команда формирует список фамилий учеников:
$ awk '{print substr($1,3)}' grade.txt
Tansley
Lulu
Bunny
Troll
Tansley
Передача строк из интерпретатора shell утилите awk
Очень часто утилита awk получает входные данные не из файла, а по каналу от других команд. Рассмотрим несколько примеров обработки строк, поступающих из канала.
В первом примере команда echo передает строку "Stand‑by" утилите awk, которая вычисляет ее длину:
$ echo "Stand‑by" | awk '{print length($0)}'
8
Во втором примере утилита awk получает строку с именем файла и возвращает имя файла без расширения:
$ echo "mydoc.txt" | awk '{print substr($STR,1,5)}'
mydoc
Следующая команда возвращает только расширение файла:
$ echo "mydoc.txt" | awk '{print substr($STR,7)}'
txt
9.2.11. Escape–последовательности
При работе со строками и регулярными выражениями нередки случаи включения в шаблон поиска непечатаемых символов (таких как символ новой строки либо табуляции) или же символов со специальным значением в утилите awk (любой из метасимволов). Такие символы создаются с помощью управляющих Escape–последовательностей, признаком которых является обратная косая черта в начале.
Например, шаблон поиска открывающей фигурной скобки выглядит так:
/|/
В табл. 9.5 перечислены Escape–последовательности, распознаваемые утилитой awk.
Таблица 9.5. Escape–последовательности утилиты awk
b
Возврат на одну позицию (забой)
f
Прокрутка страницы
n
Новая строка
r
Возврат каретки
t
Горизонтальная табуляция
ddd
Восьмеричный код символа
c
Любой другой специальный символ. Например, запись \ соответствует символу обратной косой черты
В следующей команде сначала отображается фраза «May Day», в которой слова разделены символом табуляции, а затем выводятся два символа новой строки, вследствие чего образуется пустая строка. Потом отображается слово «May», а за ним -cлово «Day», каждая буква которого представлена ASCII–кодом: 'D' – 104, 'а'– 141,'у'– 171.
$ awk BEGIN {print "MaytDaynnMay 104141171"}'
May Day
May Day
9.2.12. Команда printf
Во всех примерах, с которыми мы ознакомились, данные выводились на экран с помощью команды print без какого‑либо форматирования. В awk имеется намного более мощная команда printf, аналог одноименной функции языка С, позволяющая задавать правила форматирования выходных данных.
Базовый синтаксис команды таков:
printf "строка_формaтирования", аргументы
Строка форматирования может включать как литеральные символы, записываемые в выходной поток без изменения, так и спецификаторы форматирования (табл. 9.6), каждый из которых задает способ интерпретации соответствующего ему аргумента.
Таблица 9.6. Спецификаторы форматирования
%c
Символ ASCI I; если аргумент является строкой, выводится первый символ строки
%d, %i
Целое число
%e
Число с плавающей точкой в формате [-]d.dddddde[+-]dd
%E
Аналогичен спецификатору %e, но знак экспоненты представлен символом 'E', а не 'e'
%f
Число с плавающей точкой в формате [~}ddd.dddddd
%G
Тип преобразования будет %e или %f в зависимости от того, какой результат короче; вывод незначащих нулей подавляется Аналогичен спецификатору %g, но экспоненциальный формат будет представлен спецификатором %E, а не %e .
%o
Восьмеричное число без знака
%s
Строка символов
%x
Шестнадцатеричное число без знака (используются шестнадцатеричные
цифры а, b, с, d, e, f)
Аналогичен спецификатору %x, но используются шестнадцатеричные
цифры А, В, С, D, E, F
%%
Отображается символ '%', интерпретации аргумента не происходит
В состав спецификаторов форматирования могут входить различные модификаторы, определяющие дополнительные особенности форматирования (табл. 9.7). Модификатор помещается между символом '%' и управляющим символом.
Таблица 9.7. Дополнительные модификаторы
-
Аргумент выравнивается по левому краю поля вывода (по умолчанию принято правостороннее выравнивание)
пробел
Если аргумент является положительным числом, перед ним ставится
пробел, а если отрицательным – знак минус
+
Если аргумент является числом, ему всегда предшествует знак плюс,
даже если это положительное число
#
Выбирается альтернативная форма спецификатора:
%о – восьмеричному числу предшествует ведущий ноль;
%x – шестнадцатеричному числу предшествует запись 0x;
%x – шестнадцатеричному числу предшествует запись Оx;
%e, %E, %f – число всегда содержит десятичную точку;
%g, %G – вывод незначащих нулей не подавляется
0
Если длина поля вывода больше, чем число символов в представлении
аргумента, аргумент дополняется нулями, а не пробелами
ширина
Ширина поля вывода; если длина поля больше, чем число символов
в представлении аргумента, аргумент по умолчанию дополняется
пробелами
. точность
Точность представления аргумента:
%е, %е, %f – число символов после десятичной точки;
%g, %g – максимальное число значащих цифр;
%d, %o, %i, %u, %x, %X – минимальное число выводимых цифр;
% s – максимальное число выводимых символов
Преобразование символов
Чтобы узнать, ASCII–код какого символа равен 65, можно с помощью команды echo направить строку "65" утилите awk и затем передать ее в качестве аргумента команде printf со спецификатором %с. Команда printf выполнит преобразование автоматически:
$ echo "65" | awk '{printf "%cn", $0}'
A
Как видите, это символ 'A'. Обратите внимание на наличие в команде символа новой строки (n). Необходимость в нем объясняется тем, что команда printf по умолчанию не записывает этот символ в выходной поток. Если не указать Escape–последовательность n, сразу после буквы 'А' в той же строке будет отображено приглашение интерпретатора shell, что может смутить пользователя, работающего в данный момент за терминалом.
Конечно, код символа может быть указан непосредственно в команде printf:
$ awk 'BEGIN {printf "%cn", 65}'
A
Форматированный вывод
Предположим, необходимо отобразить имена учеников и их идентификаторы, причем имена должны быть выровнены по левому краю и размещаться в поле длиной 15 символов. В конце строки форматирования стоит символ новой строки (n(,
служащий для разделения записей. Выводимые данные будут размещаться в двух колонках:
$ awk '{printf "%-l5s %dn", $1, $3}' grade.txt
M. Tansley 48311
J. Lulu 48311
P. Bunny 4 8
J. Troll 4842
L. Tansley 4712
9.2.13. Передача переменных утилите awk
Переменные можно создавать не только в сценарии awk, но и непосредственно в командной строке. Формат вызова утилиты awk в этом случае таков:
awk 'сценарий' переменная=значение входной_файл
В следующем примере переменная AGE создается в командной строке и инициализируется значением 10. Показанная команда находит студентов, возраст которых не превышает 10 лет.
$ awk '{if($5 < AGE) print $0}' AGE=10 grade.txt
M. Tansley 05/99 48311 Green 8 40 44
J. Lulu 06/99 48317 green 9 24 26
Рассмотрим более сложный пример. Системная команда df отображает информацию о смонтированных файловых системах. Результаты ее работы по умолчанию имеют следующий формат:
Имя
Число блоков .
Занято блоков
Свободно блоков
Процент
Точка монтирования
файловой системы
занятых
блоков
Столбец
1
2
3
4
5
6
Чтобы узнать, в какой из имеющихся файловых систем объем свободного пространства ниже критической отметки, следует передать выходные данные команды df утилите awk и последовательно сравнить значения в четвертом столбце с пороговым значением. В следующей командной строке пороговое число задано в виде переменной TRIGGER, которая равна 56000:
$ df -k | awk '$4 ~ /^[0-9]/ {if($4 < TRIGGER) print $1"t"$4)' TR1GGER=56000
/dos 55808 /apps 51022
Опция – к команды df устанавливает размер блока равным 1 Кб (наиболее привычный для пользователя режим), так как в системе может быть задан другой размер блока. Проверка $4 ~ /^[0-9]/ позволяет отсечь заголовок отчета команды df, содержащий в четвертом столбце строку "Available".
Вот еще один пример. Команда who выводит сведения о пользователях, зарегистрировавшихся в системе. В первом столбце выходных данных этой команды отображаются регистрационные имена пользователей, а во втором – имена терминалов, к которым подключены эти пользователи. С помощью утилиты awk вы можете
отфильтровать результаты работы команды who и узнать к какому терминалу подключены: ,
$ who | awk '{if($1==user) print $1 " you are connected to " $2}' user=$LOGNAME
root you are connected to ttyp1
Здесь локальная переменная user инициализируется значением переменой среды $LOGNAME, которая хранит регистрационное имя текущего пользователя.
9.2.14. Файлы сценариев
Если последовательность команд awk слишком велика, чтобы вводить ее в командной строке, или предназначена для многократного использования, целесообразно поместить ее в отдельный файл сценария. Преимуществом сценариев является также возможность добавления комментариев, благодаря которым вы сможете быстро вспомнить назначение той или иной программы.
Давайте возьмем один из рассмотренных ранее примеров и преобразуем его в файл сценария awk. В частности, следующая, уже знакомая нам, команда подсчитывает суммарный рейтинг учеников секции:
$ awk 'tot+=$6; END {print "Club student total points; " tot}' grade.txt
Создадим на ее основе файл, который назовем student_tot.awk. Расширение awk является общепринятым соглашением относительно именования файлов сценариев awk. Вот текст этого файла:
#!/bin/awk -f
#Все строки комментариев должны начинаться с символа '#'.
#Имя файла: student_tot.awk
#Командная строка: student_tot.awk grade.txt
#Вычисление суммарного и среднего рейтинга учеников секции.
#Сначала выводим заголовок.
BEGIN {
print "Student Date Member Grade Age Points Max"
print "name joined number gained point available"
print "================================================================="
}
# Суммируем рейтинг учеников.
tot+=$6
# В завершение выводим суммарный и средний рейтинг.
END {
print "Club student total points: " tot print "Average club student points: " tot/NR }
В этом сценарии мы формируем заголовок отчета и вычисляем не только суммарный рейтинг, но и средний рейтинг учеников секции, который получается путем деления переменной tot на переменную NR, содержащую число записей во входном файле. Как видите, на языке awk можно писать полноценные программы, содержащие команды, комментарии, а также пустые строки и дополнительные пробелы, предназначенные для повышения удобочитаемости текста сценария.
Ключевым моментом сценария является первая строка, выглядящая как комментарий:
#!/bin/awk -f
На самом деле это своеобразная системная инструкция, указывающая, какая программа должна выполнять данный сценарий– Подобная инструкция должна быть первой строкой любого сценария. Общий ее формат таков:
#!/путь/программа [командная_строка]
Выражение #! называется "магической" последовательностью. Подразумевается, что, во–neрвых, система, в которой запускается, сценарий, распознает эту последовательность, а, во–вторых, указанная программа воспринимает символ '#' как признак комментария. Это справедливо в отношении всех современных интерпретаторов shell, а также программ perl, awk. и sed, для которых чаще всего создаются автономные сценарии.
Когда происходит запуск исполняемого файла, система проверяет, начинается ли он с "магической" последовательности. Если нет, значит, файл содержит машинные коды и выполняется непосредственно. Если же обнаружено выражение #!, то это файл сценария. В таком случае происходит следующее:
1. Первая строка сценария заменяет собой командную строку, из нее удаляется "магическая" последовательность;
2. Предыдущая командная строка передается новой командной строке в качестве аргумента.
В нашем случае это означает, что при запуске сценария вместо команды
$ student_tot.awk grade.txt
в действительности выполняется такая команда:
$ /bin/awk -f student_tot.awk grade.txt
Опция -f утилиты awk говорит о том, что выполняемые команды находятся в указанном вслед за ней файле.
После создания файл student_tot.awk необходимо сделать исполняемым с помощью команды
$ chmod u+x student_tot.awk
Вот результаты работы сценария:
$ student_tot.awk grade.txt
Student name
Date joined
Member number
Grade
Age
Points gained
Max
point available
M. Tansley
05/99
18311
Green
8
40
44
J. Lulu
06/99
48317
green
9
24
26
P. Bunny
02/99
48
Yellow
12
35
28
J. Troll
07/99
4842
Brown-3
12
26
26
L. Tansley
05/99
4712
Brown~2
12
30
28
Club student
total points: 155
Average Club
Student
points: 31
Использование переменной FS в сценариях awk
Если просматривается файл, в котором разделителем полей является символ, отличный от пробела, например '#' или ':', это легко учесть, указав в командной строке опцию ' -F:'
$ awk -F: '{print $0}' входной_файл
Аналогичную функцию выполняет переменная FS, которую можно установить непосредственно в сценарии. Следующий сценарий обрабатывает файл /etc/passwd, выводя на экран содержимое первого и пятого полей, включающих соответственно имя пользователя и описание роли пользователя в системе. Поля в этом файле разделены символом двоеточия, поэтому в процедурной части шаблона begin устанавливается переменная FS, значение которой заключается в двойные кавычки:
$ cat passwd.awk
#!/bin/awk -f
# Имя файла: student_tot.awk
# Командная строка: passwd.awk /etc/passwd
# Вывод содержимого первого и пятого полей файла паролей.
BEGIN {
FS=":" }
{print $1"t"$5}
Вот возможные результаты работы этого сценария:
$ passwd.awk /etc/passwd
root Special Admin login
xdm Restart xda Login
sysadm Regular Admin login
daemon Daemon Login for daemons needing permissions
Передача переменных сценариям awk
При передаче переменной сценарию awk формат командной строки таков:
awk файл_сценария переменная=значение входной_файл
Следующий небольшой сценарий сравнивает количество полей в каждой записи входного файла с заданным в командной строке значением. Текущее количество полей хранится в переменной NF. Сравниваемое значение передается сценарию в виде переменной МАХ.
$ cat fieldcheck.awk
#!/bin/awk -f
#Имя файла: fieldcheck.awk
#Командная строка: fieldcheck.awk MAX=n [FS=<разделитель>] имя_файла
#Проверка числа полей в записях файла.
{if(NF != МАХ)
print "line " NR " does not have " MAX " fields"}
При запуске этого сценария вместе с файлом /etc/passwd, содержащим семь полей, необходимо указать следующие параметры:
$ fieldcheck.awk МАХ=7 FS=":" /etc/passwd
Следующий сценарий выводит информацию об учениках, чей возраст ниже значения, заданного в командной строке:
$ cat age.awk
#!/bin/awk -f
# Имя файла: age.awk
# Командная строка: age.awk AGE=n grade.txt
# Вывод информации об учениках, чей возраст ниже заданного,
{if($5 < AGE) print $0}
$ age.awk AGE=10 grade.txt
M. Tansley 05/99 48311 Green В 4 0 4 4 J. Lulu 06/99 18317 green 9 24 26
9.2.15. Массивы
Изучая функцию split(), мы говорили о том, каким образом ее можно использовать для преобразования строки символов в массив. Повторим соответствующий пример:
$ awk 'BEGIN {print split("123#456#678", myarray, "#")}'
3
Функция split() возвращает число элементов созданного массива myarray. Внутренняя структура массива myarray в этом случае такова:
myarray[1]="123" myаrray[2]="456" myarray[3]="678"
При работе с массивами в awk не требуется заранее их объявлять. Не нужно также указывать количество элементов массива. Для доступа к массиву обычно применяется цикл for, синтаксис которого следующий:
for (элемент in массив) print массив[элемент]
В показанном ниже сценарии функция split() разбивает заданную строку на элементы и записывает их в массив myarray, а в цикле for содержимое массива выводится на экран:
$ cat arraytest.awk
#! /bin/awk -f
#Имя файла: arraytest.awk
#Командная строка: arraytest.awk /dev/null
#Вывод элементов массива.
BEGIN {
record="l23#456#789";
split(record, myarray, "#") } END {
for (i in myarray) print myarray[i]
}
Для запуска сценария в качестве входного файла следует указать устройство /dev/null. Если этого не сделать, утилита awk будет ожидать поступления данных с клавиатуры.
$ arraytest.awk /dev/null
123
789
Статические массивы
В предыдущем примере массив формировался динамически. Следующий пример является более сложным и демонстрирует, как создавать в сценарии статические массивы. Ниже показан входной файл grade_student.txt, включающий информацию об учениках нашей секции каратистов. Записи файла содержат два поля: первое – название пояса, которым владеет ученик, второе – категория ученика (взрослый, юниор). Разделителем полей служит символ '#'.
$ cat grade_student.txt
Vellow#Junior
Orange#Senior
Yellow#Junior
Purple#Junior
Brown-2#Junior
White#Senior
Drange#Senior
Red#Junior
Brown-2#Senior
Yellow#Senior
Red#Junior
Blue#Senior
Green#Senior
Purple#Junior
White#Junior .
Наша задача заключается в том, чтобы прочитать файл и получить следующие сведения:
1. Сколько учеников имеют желтый, оранжевый и красный пояса?
2. Сколько взрослых и детей посещают секцию? Рассмотрим такой сценарий:
$ cat belts.awk
#!/bin/awk -f
#Имя файла: belts.awk
#Командная строка: belts.awk grade_student.txt
#Подсчет числа учеников, имеющих желтый, оранжевый и красный пояса,
#а также количества взрослых и юных членов секции.
# Задание разделителя и создание массивов.
BEGIN (
FS="#"
# Создание массива, индексами которого являются
#названия поясов. belt["Yellow"] belt["Orange"] belt["Red"]
#Создание массива, индексами которого являются
#названия категорий учеников. student["Junior"]
student["Senior"]
}
#Если значение первого поля совпадает с индексом массива belt,
#содержимое соответствующего элемента массива увеличивается на единицу. {for(colour in belt)
{if ($1==colour)
belt[colour]++ ) }
# Если значение второго поля совпадает с индексом массива student,
# содержимое соответствующего элемента массива увеличивается на единицу, { for (senior_or_junior in student) { if ($2==senior_or_junior)
student [senior_or_junior] ++
) }
# Вывод полученных результатов
END {
for (colour in belt)
print "The club has", belt[colour], colour, "belts" for(senior_or_junior in student)
print "The club has", 'student[senior_or_junior], senior_or_junior, "students" }
В процедурной части шаблона begin в переменную FS записывается символ '#" -pазделитель полей во входном файле. Затем создаются два массива, belt и student, индексами которых являются соответственно названия поясов и названия категорий учеников. Оба массива остаются неинициализированными, их тип не определен.
В первом цикле for значение первого поля каждой записи входного файла сравнивается с названием индекса массива belt (индекс равен "Yellow", "Orange" или "Red"). Если обнаруживается совпадение, выполняется приращение элемента, хранящегося в массиве по этому индексу. Поскольку операция ++ (инкремент) определена для целочисленных значений, утилита awk считает, что массив целочисленный, и инициализирует его элементы значением 0.
Во втором цикле for значение второго поля сравнивается с названием индекса массива student (индекс равен "Junior" или "Senior"). Результат операции вычисляется так же, как и в первом цикле.
В процедурной части шаблона end осуществляется вывод итоговых результатов.
$ belts.awk grade_student.txt
The club has 2 Red belts
The club has 2 Orange belts
The club has 3 Yellow belts
The club has 7 Senior students
The club has 8 Junior students
9.3. Заключение
Язык программирования awk может показаться сложным для изучения, но если осваивать его на примерах употребления отдельных команд и небольших сценариев, то процесс обучения не будет слишком трудным. В данной главе мы изучили основы awk, не углубляясь в детали, но, тем не менее, получили необходимый минимум сведений, относящихся к этому языку. Утилита awk является важным инструментом shell–программирования, и чтобы применять ее, вам не обязательно быть экспертом по языку awk.
ГЛАВА 10
Работа с редактором sed
Редактор sed относится к текстовым редакторам потокового типа и не является интерактивной программой. Он предназначен для обработки текстовых данных, поступающих из файла или стандартного входного потока. Отличительной особенностью редактора sed является то, что он позволяет модифицировать файлы в фоновом режиме. Задача пользователя сводится к тому, чтобы задать последовательность команд редактирования, а всю остальную работу выполнит сам редактор. Кроме того, sed допускает внесение всех изменений за один проход, а это превращает его в достаточно эффективный и, что важнее всего, быстродействующий инструмент редактирования текста.
В этой главе рассматриваются следующие темы:
• синтаксис команд sed;
• поиск строк по номерам и с использованием регулярных выражений;
• изменение входного текста и добавление текста в выходной поток;
• примеры команд и сценариев sed.
Команды sed вводятся в командной строке либо размещаются в файле сценария подобно тому, как это делается в случае с утилитой awk. При использовании sed важно помнить следующее: этот редактор оставляет без изменения исходный файл независимо от того, какая команда выполняется. Копия входного потока помещается в буфер редактирования, а все изменения направляются на экран или переадресуются в выходной файл.
Поскольку редактор sed не предназначен для работы в интерактивном режиме, текстовые строки, подлежащие изменению, отбираются либо по номерам, либо на основании соответствия регулярному выражению.
10.1. Чтение и обработка данных в sed
Общая схема работы редактора sed такова:
1. Редактор последовательно извлекает строки текста из файла или стандартного входного потока и копирует их в буфер редактирования.
2. Затем он считывает первую команду из командной строки или сценария, осуществляет поиск строки с указанным номером или строки, соответствующей шаблону, и применяет к ней эту команду.
3. Второй пункт повторяется до тех пор, пока не будет исчерпан список команд.
10.2. Вызов редактора sed
Вызвать редактор sed можно тремя способами:
1. Ввести набор команд sed в командной строке.
2. Поместить набор команд sed в файл и передать его редактору sed в командной строке.
3. Поместить набор команд sed в файл сценария и сделать его выполняемым. Если редактор sed вызывается для выполнения одиночных команд, формат
командной строки будет таким:
sed [опции] 'команды' входной_файл
Как и в awk, не забудьте заключить команды в одинарные кавычки, чтобы защитить от интерпретации находящиеся в них специальные символы.