Чтение онлайн

ЖАНРЫ

UNIX — универсальная среда программирования
Шрифт:
Строки

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

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
, чем засорять конец программы кавычками.

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

n
, и произвольно длинный список имен файлов. Для этого требуется определенная техника программирования на языке
shell
; которую мы рассмотрим в следующей главе.

Служебная программа-календарь на языке
awk

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

Задача состоит в создании системы, посылающей вам каждое утро почту с напоминанием об ожидаемых событиях. (Возможно, такая календарная система уже есть; см. руководство по

calendar(1)
.) В этом разделе применяется иной подход. Вам будут перечислены события, происходящие сегодня и, кроме того, предстоящие сегодняшние и завтрашние события. Правильный учет праздников и выходных оставлен вам в качестве упражнения.

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

calendar
в каталоге
/usr/you
:

$ cat calendar

Sep 30 день рождения мамы

Oct 1 обед с Джо, полдень

Oct 1 встреча в 16:00

$

Далее, необходимо уметь просматривать календарь, отыскивая определенную дату. Существует масса вариантов; мы остановимся на языке

awk
, поскольку с его помощью легче выполнять арифметические операции по переходу от одной даты к другой, однако для этой цели подходят и другие программы, например
sed
и
egrep
. Конечно, строки, выбранные из файла
calendar
, посылаются командой
mail
.

Поделиться с друзьями: