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
for (i = 1; i <= N; i++) # make a string of blanks
blanks = blanks " "
}
{
if ((n = length($0)) <= N)
print
else {
for (i = 1; n > N; n -= N) {
printf "%s\\\n", substr($0,i,N)
i += N;
}
printf "%s%s\n" , substr(blanks, 1, N-n), substr($0, I)
}
} '
На языке
awk
нет явной операции конкатенации строк; строки соединяются, если они следуют подряд. Вначале blanks
является пустой строкой. Цикл в части BEGIN
создает длинную строку пробелов конкатенацией: каждый шаг цикла прибавляет еще один пробел к концу строки blanks
. Во втором цикле входная строка разбивается на части, пока оставшаяся часть не станет достаточно короткой. Как и в языке Си, операцию присваивания можно использовать в качестве выражения, поэтому в конструкцииif ((n=length($0)) <= N)...
длина входной строки присваивается
n
до проверки значения. Обратите внимание на скобки.Упражнение 4.10Измените программу
fold
так, чтобы разрыв строки происходил на пробеле или символе табуляции, а не посреди слова. Сделайте эту программу пригодной и для длинных слов.Взаимодействие с интерпретаторомДопустим, что вы намереваетесь написать программу
field n
. Эта программа будет печатать n-е поле каждой входной строки так, чтобы можно было, например, задать:$ who | field 1
для печати только имен, под которыми пользователи входят в систему. Язык
awk
явно предоставляет возможность выбора полей. Наша основная задача — передать номер n программе awk
. Ниже приведено одно из возможных решений:$ awk '{print $'$1'}'
Здесь
$1
открыто (не внутри каких либо кавычек), и поэтому становится номером поля, доступным в программе awk
. При ином решении используются кавычки:awk "{print \$$1}"
Аргумент обрабатывается интерпретатором, поэтому
\$
становится $
, а $1
заменяется на значение n
. Мы предпочитаем решение с апострофами (одиночными кавычками), поскольку при использовании кавычек в типичной программе awk
появится слишком много символов \
.Другим примером может служить программа
addup n
, суммирующая значения n-го поля:awk '{s += $'$1'}
END {print s}'
В третьем примере вычисляются отдельные суммы значений каждого n-го поля и полная сумма:
awk '
BEGIN { n = '$1' }
{ for (i=1; i <= n; i++)
sum[i] += $1
}
END { for(i = 1; i <= n; i++)
{
printf "%6g ", sum[i]
total += sum[i]
}
printf "; total = %6g ", total
}'
Нам удобнее было использовать часть
BEGIN
для засылки значения в переменную n
, чем засорять конец программы кавычками.