. Мы предпочитаем решение с апострофами (одиночными кавычками), поскольку при использовании кавычек в типичной программе
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
, чем засорять конец программы кавычками.
Основная трудность во всех приведенных выше примерах состоит не в том, чтобы следить за кавычками (хотя и это хлопотно), а в том, что программы, составленные показанным способом, могут читать только свой стандартный входной поток. Нет никакой возможности передать им сразу и параметр
n
, и произвольно длинный список имен файлов. Для этого требуется определенная техника программирования на языке
shell
; которую мы рассмотрим в следующей главе.
Служебная программа-календарь на языке
awk
В нашем последнем примере демонстрируются ассоциативные массивы, а также иллюстрируется взаимодействие с интерпретатором и частично показывается процесс разработки программы.
Задача состоит в создании системы, посылающей вам каждое утро почту с напоминанием об ожидаемых событиях. (Возможно, такая календарная система уже есть; см. руководство по
calendar(1)
.) В этом разделе применяется иной подход. Вам будут перечислены события, происходящие сегодня и, кроме того, предстоящие сегодняшние и завтрашние события. Правильный учет праздников и выходных оставлен вам в качестве упражнения.
Прежде всего нужно предусмотреть место, где будет храниться календарь. Имеет смысл разместить его в файле с именем
calendar
в каталоге
/usr/you
:
$ cat calendar
Sep 30 день рождения мамы
Oct 1 обед с Джо, полдень
Oct 1 встреча в 16:00
$
Далее, необходимо уметь просматривать календарь, отыскивая определенную дату. Существует масса вариантов; мы остановимся на языке
awk
, поскольку с его помощью легче выполнять арифметические операции по переходу от одной даты к другой, однако для этой цели подходят и другие программы, например
sed
и
egrep
. Конечно, строки, выбранные из файла
calendar
, посылаются командой
mail
.
Наконец, вам придется научиться автоматически и безотказно просматривать календарь каждый день, скажем, рано утром. Это можно сделать с помощью команды
at
, о которой упоминалось в гл. 1.
Если ограничить календарь таким форматом, в котором каждая строка начинается с названия месяца и числа (как это делает команда
date
),
то составить первый вариант программы календаря нетрудно:
$ date
Thu Sep 29 15:23:12 EDT 1983
$ cat bin/calendar
# calendar: version 1 - today only
awk <$HOME/calendar '
BEGIN { split ("'"`date`"'", date) }
$1 == date[2] && $2 == date[3]
' | mail $NAME
$
Функция в части
BEGIN
разбивает дату, выдаваемую командой
date
, и заносит ее в массив; второй и третий элементы массива — месяц и число. Мы предполагаем, что в переменной интерпретатора
NAME
находится имя, под которым вы вошли в систему. Вы заметили, какая нужна сложная последовательность кавычек, чтобы "поймать" результат действия команды
date
в середине строки программы
awk
. Более простым решением является передача даты в первой строке входного потока:
$ cat /bin/calendar
# calendar: version 2 - today only, no quotes
(date; cat $HOME/calendar) |
awk '
NR == 1 { mon = $2; day = $3 } # set the date
NR > 1 && $1 == mon && $2 == day # print calendar lines
' | mail $NAME
$
На следующем шаге требуется так изменить программу, чтобы искать сообщение с завтрашней датой так же, как и с сегодняшней. Наибольшие усилия затрачиваются на прибавление единицы к сегодняшнему числу. Но в конце месяца нужно перейти к следующему месяцу, а число приравнять единице. Конечно, число дней в разных месяцах различно. Именно здесь на помощь приходит ассоциативный массив. Два массива
days
и
nextmon
, индексами которых служат названия месяцев, содержат число дней месяца и название следующего месяца. Например,
days["Jan"]
равно 31, a
nextmon["Jan"]
есть
Feb
. Вместо того чтобы написать множество операторов типа
days["Jan"] = 31; nextmon["Jan"] = "Feb"
days["Feb"] = 28; nextmon["Feb"] = "Mar"
...
мы воспользуемся функцией
split
для преобразования удобно записываемой структуры данных в то, что требуется:
$ cat calendar
# calendar: version 3 -- today and tomorrow
awk <$HOME/calendar '
BEGIN {
x = "Jan 31 Feb 28 Mar 31 Apr 30 May 31 Jun 30 " \
"Jul 31 Aug 31 Sep 30 Oct 31 Nov 30 Dec 31 Jan 31"