Главная » Программирование для UNIX » Программирование в оболочке UNIX

0

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

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

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

«устойчивых» программ, которые могли бы обработать некорректные

входные данные и выдать полезную информацию в случае, если что-то  пошло не так.

Переделываем  команду cal

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

$ cal

usage: cal  [month]  year                 Пока все хорошо

$ cal  october  1983

Bad argument                                      Уже не так хорошо

$ cal  10 1983

October  1983

S   M  Tu    W  Th    F    S 1

2   3   4   5   6   7   8

9 10 11 12 13 14 15

16 17 18 19 20 21 22

23 24 25 26 27 28 29

30 31

$

Досадно, что месяц должен указываться в числовом виде. К тому  же, оказывается, что  cal  10 выводит календарь для  десятого года  вместо того, чтобы  делать это для текущего октября, и для того, чтобы  полу чить  календарь на один месяц, обязательно надо указывать год.

Обратите внимание на  важный момент: какой  бы интерфейс ни  предоставляла команда cal,  можно изменить его,  не  изменяя саму  программу.  Можно поместить в личный каталог  /bin  команду, которая преобразовывала бы удобный для  вас синтаксис в такой, который требуется настоящей программе cal. Свою версию можно также назвать cal  – меньше придется запоминать.

В первую очередь следует составить план того, что должна выполнять cal. Будем стремиться к тому, чтобы  программа стала разумной. Она должна воспринимать название месяца, а не его номер. Получив два аргумента, она должна вести  себя  в точности так  же, как старая версия, и, кроме того, преобразовывать названия месяцев в номера. Если  задан только один аргумент, программа должна вывести календарь на месяц или  год  (по обстановке), если  же  аргументы не заданы, новая версия cal  должна напечатать календарь на текущий месяц – очевидно, что это самое  частое  применение такой команды. Итак, задача заключается  в  том,   чтобы  понять, сколько задано  аргументов,  затем отобразить их согласно правилам стандартной cal.

Для  таких решений удобно использовать оператор case:

case  слово  in

шаблон)             команды ;;

шаблон)             команды ;;

esac

Оператор case сравнивает слово  с шаблонами, двигаясь сверху вниз, и выполняет команды,  соответствующие первому (и  только первому) подходящему шаблону. Шаблоны записываются по общим  правилам, принятым в оболочке для имен  файлов с небольшими дополнениями. Каждое действие завершается двойной точкой с запятой ;;. (Последняя пара  ;; не обязательна, но оставим ее для удобства редактирования.)

Новая версия cal определяет количество аргументов, обрабатывает названия месяцев и  вызывает настоящую программу cal.  Переменная оболочки $# хранит количество аргументов, с которым был вызван командный файл. Другие специальные переменные оболочки перечислены в табл. 5.1.

Таблица 5.1. Встроенные переменные оболочки

Переменная

Смысл

$#

$*

$@

$–

$?

$$

$!

$HOME

$IFS

$MAIL

$PATH

$PS1

$PS2

количество аргументов все аргументы

аналогично $*, см. раздел 5.7 параметры, переданные оболочке

возвращает код завершения последней выполненной команды идентификатор процесса оболочки

идентификатор процесса для последней команды, запущенной с &

аргумент по умолчанию для команды cd

список символов, которые разделяют слова в аргументах

файл, при  изменении которого выводится сообщение «you  have mail»

список каталогов для поиска команд

строка приглашения на ввод,  по умолчанию $

строка приглашения  на  продолжение  командной  строки, по умолчанию >

$ cat  cal

# cal:  улучшенный  интерфейс к  /usr/bin/cal case  $# in

case  $m  in

jan*|Jan*)           m=1  ;; feb*|Feb*)           m=2  ;; mar*|Mar*)           m=3  ;; apr*|Apr*)           m=4  ;; may*|May*)           m=5  ;; jun*|Jun*)           m=6  ;; jul*|Jul*)           m=7  ;; aug*|Aug*)           m=8  ;; sep*|Sep*)           m=9  ;; oct*|Oct*)           m=10  ;; nov*|Nov*)           m=11  ;; dec*|Dec*)           m=12  ;;

[1–9]|10|11|12) ;;            # числовое представление  месяца

*)                           y=$m;  m="";;  #  только год esac

/usr/bin/cal  $m  $y                    # запустить  настоящую  программу

$

Первый оператор case проверяет, сколько задано аргументов ($#), и выбирает соответствующее действие. Последний шаблон первого оператора case (*) – это «ловушка», если количество аргументов не равно  ни 0, ни 1, то выполняется последний оператор case. (Шаблоны просматриваются сверху вниз, поэтому такой собирающий все шаблон должен быть последним.) Он присваивает переменным m и y значения месяца и года.  Получив два  эти аргумента, новая версия cal действует так же, как и исходная.

В первом блоке  оператора case присутствует такой оператор:

set `date`

По внешнему виду трудно определить, что делает этот оператор, но, если выполнить его, все станет очевидным:

$ date

Sat  Oct   1 06:05:18  EDT  1983

$ set  `date`

$ echo $1

Sat

$ echo $4

06:05:20

$

Команда set встроена в оболочку, и это команда, которая выполняет очень  многие  действия. При  отсутствии аргументов она  отображает значения переменных окружения, как было  показано в главе 3. Одиночные аргументы устанавливают значения переменных $1, $2 и т. д. Таким образом, set  `date` устанавливает $1 в день  недели, $2 в название месяца и т. д. Поэтому первый оператор case программы cal  уста навливает текущий месяц и год в случае отсутствия аргументов; если

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

Команда set также распознает различные параметры, самыми распространенными из них являются –v и –x; они включают режим отображения на терминале команд по мере  их обработки оболочкой. Эти пара метры необходимы для отладки сложных программ.

Остается преобразовать название месяца в  текстовой форме  в номер. Такую операцию выполняет второй оператор case, который практически  не требует пояснений, все и так очевидно. Единственная тонкость заключается в использовании в шаблонах символа |, который, как и в egrep,  обозначает  альтернативу: big|small соответствует или  big,  или  small. Конечно же, возможна и запись [jJ]an* и т. п. Программа допускает  введение  всех  букв названия месяца в нижнем регистре  (большинство команд воспринимают ввод в нижнем регистре) или  же  первая  буква  названия может быть  заглавной (именно в  таком формате выводит данные команда date). В табл. 5.2 объяснено, каким образом устанавливается соответствие каждому из шаблонов оболочки.

Таблица 5.2. Шаблоны оболочки

Символ

Соответствие

*

?

[ccc] "…"

\c

a|b

/

.

соответствует любой строке, в том числе и пустой соответствует любому отдельному символу соответствует любому из символов в ccc

[a–d0–3]  эквивалентно [abcd0123]

точно соответствует …; кавычки защищают специальные символы.

Аналогично действует ‘…’

буквально соответствует с

(только для операторов case) соответствует или a, или b

для имен файлов соответствует только явному знаку / в выражении; для оператора case обрабатывается подобно обычным символам

будучи первым символом имени файла, соответствует только явной

. в выражении

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

Наконец, последняя строка вызывает /usr/bin/cal (то есть настоящую cal) с преобразованными аргументами. Новая версия cal делает то, что ожидает от нее человек, впервые столкнувшийся с такой программой:

$ date

Sat  Oct   1 06:09:55  EDT  1983

$ cal

October  1983

S   M  Tu    W  Th    F    S 1

2   3   4   5   6   7   8

9 10 11 12 13 14 15

16 17 18 19 20 21 22

23 24 25 26 27 28 29

30 31

$ cal  dec

December  1983

S   M   Tu   W   Th   F   S

1   2   3

4   5   6   7   8   9 10

11 12 13 14 15 16 17

18 19 20 21 22 23 24

25 26 27 28 29 30 31

$

А команда cal  1984 выводит календарь на весь 1984  год.

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

И,  чтобы  закончить с оператором case, обратим внимание на отличие правил установления соответствия шаблону оболочки от аналогичных правил в ed и его производных. Ведь наличие двух  типов  шаблонов означает, что необходимо изучить два набора  правил и что их обработка осуществляется  двумя  фрагментами  кода.  Некоторые расхождения появились исключительно из-за неудачного выбора и так и не были исправлены, например нет  никакого  объяснения (за  исключением совместимости с уже  забытым прошлым) тому, что в ed соответствие любому символу задается посредством «.», а в оболочке – «?».  Но есть и шаблоны, выполняющие разные задачи.  Регулярные выражения редактора ищут  символьную строку, которая может  встретиться  в любом месте  строки, а для «привязки» поиска к началу и концу строки используются специальные символы ^ и $.  Однако для  имен  файлов хотелось бы иметь такую привязку по умолчанию (поскольку это самый распространенный случай). Вводить

$ ls ^?*.c$                   В таком виде не работает

вместо

$ ls *.c

было бы очень неудобно.

Упражнение 5.1. Если пользователи предпочитают усовершенствованную  вами новую  версию cal, то как сделать ее глобально доступной? Что надо сделать для того, чтобы поместить ее в /usr/bin? ~

Упражнение 5.2. Стоит ли подправить cal таким образом, чтобы  cal  83 выводила календарь на 1983  год? Если да, то как тогда вывести календарь на 83 год? ~

Упражнение 5.3.  Измените cal  так, чтобы можно было  задавать сразу  несколько месяцев, например

$ cal  oct  nov

или диапазон месяцев:

$ cal  oct  dec

Если сейчас декабрь, и вводится команда cal jan, то календарь должен быть  выведен для января  текущего или  следующего года?  Когда  же надо остановиться и прекратить улучшать cal? ~

Источник: Керниган Б., Пайк Р., UNIX. Программное окружение. – Пер. с англ. – СПб: Символ-Плюс, 2003. – 416 с., ил.

По теме:

  • Комментарии