Литмир - Электронная Библиотека
Содержание  
A
A

Таблица 4.5: Встроенные функции

awk

Ассоциативные массивы

Стандартной задачей обработки данных является получение суммарных значений для множества пар имя значение. Иными словами, по входному потоку типа

Susie 400

John  100

Mary  200

Mary  300

John  100

Susie 100

Mary  100

мы хотим получить суммарные значения для каждого имени:

John  200

Mary  600

Susie 500

Программа

awk
предлагает изящное решение этой задачи — с помощью ассоциативных массивов. Хотя обычно мы представляем себе индекс массива как целое число, в
awk
любое значение можно использовать в качестве индекса. Поэтому

    {sum[$1] += $2}

END {for (name in sum) print name sum [name]}

задает всю программу подсчета n печати сумм для пар имя значение независимо от порядка следования этих пар. Каждое имя (

$1
) служит индексом в массиве
sum
; в конце применена специальная форма цикла
for
для перебора всех элементов
sum
и их печати. Синтаксис этого варианта цикла
for
таков:

for (перем in массив)

 оператор

Хотя он может показаться вам искусственным, как цикл

for
языка
shell
, они никак не связаны. Цикл охватывает индексы массива, а не его элементы, устанавливая значение "перем" равным каждому индексу поочередно. Однако порядок появления индексов непредсказуем, поэтому может возникнуть необходимость в их сортировке. В приведенном примере выходной поток можно по конвейеру передать команде
sort
, чтобы имена шли в порядке убывания значений:

$ awk '...' | sort +1nr

Реализация ассоциативной памяти предполагает хэширование, чтобы доступ к одному элементу занимал столько же времени, сколько и к любому другому, и чтобы это время не зависело (по крайней мере для массивов средних размеров) от числа элементов в массиве.

Использование ассоциативных массивов эффективно для вычислительных задач, таких, как подсчет частоты появления слов во входном потоке:

$ cat wordfreq

awk ' { for (i = 1; i <= NF; i++) num[$i]++ }

END   {for (word in num) print word, num[word] }

' $*

$ wordfreq ch4.* | sort +1 -nr | sed 20q | 4

the 372 .CW 345 of  220 is   185

to  175 a   167 in  109 and  100

.PI  94 .P2  94 .PP  90 $     87

awk  87 sed  83 that 76 for   75

The  63 are  61 line 55 print 52

$

В первом цикле

for
выбирается каждое слово из входной строки и заполняется массив
num
, индексируемый словами. (Не путайте
$i
, обозначающее в
awk
i-е поле входной строки, с переменными языка
shell
.) После того как файл будет прочитан, во втором цикле
for
печатаются в произвольном порядке слова и частота их появления.

Упражнение 4.9

В результат действия команды

wordfreq
попали команды форматирования типа
.CW
, которые применяются для печати слов определенным шрифтом. Как избавиться от таких ненастоящих слов? Как бы вы использовали команду
tr
, чтобы программа
wordfreq
работала правильно, независимо от того, прописные или строчные буквы задействованы во входном потоке? Сравните реализацию и скорость выполнения программы
wordfreq
, конвейера из разд. 4.2 и предлагаемого ниже решения.

sed 's/[→][→]*/\

/q' $* | sort | uniq -c | sort -nr

Строки

Хотя обе команды, и

sed
и
awk
, предназначены для решения небольших задач типа выбора определенного поля, только
awk
используется в той степени, в какой предполагает настоящее программирование. Примером может служить программа, которая разбивает длинные строки, чтобы они занимали не более 80 позиций. Каждая строка, превышающая 80 символов, завершается после 80-го символа; в качестве предупреждения добавляется
\
и обрабатывается остаток строки. Хвост разбиваемой строки сдвигается к ее правому концу, а не к левому, что более удобно для программ печати, и именно поэтому мы обратимся к программе
fold
. Рассмотрим, в частности, строки из 20, а не из 80 позиций:

$ cat тест

Короткая строка

Строка немного длиннее

Эта строка еще длиннее, чем предыдущая строка

$ fold тест

Короткая строка

Строка немного длиннее

Эта строка еще длиннее,

 чем предыдущая строка

$

Вам может показаться странным, что в седьмой версии системы нет программы для добавления или удаления символов табуляции, хотя команда

pr
в System V выполняет и то и другое. Наша реализация программы
fold
использует редактор
sed
, чтобы перевести символы табуляции в пробелы и чтобы счетчик числа символов в
awk
принял правильное значение. Это хороший способ при табуляции в начале строки (что типично для языковых программ), но номер позиции сбивается, если символ табуляции оказывается в середине строки:

# fold: fold long lines

sed 's/\(->/ /g' $* |      # convert tabs to spaces

awk '

 BEGIN {

  N = 80                   # folds at column 80

54
{"b":"248117","o":1}