Шрифт:
Если вы написали программу на языке
shell
или каком-то ином языке, она может оказаться полезной и другим пользователям вашей системы. Однако требования, которым, по мнению других, должна удовлетворять программа, обычно оказываются более строгими, чем предъявляемые к ней вами. Поэтому важнейший аспект программирования на языке shell
— обеспечение надежности программы, чтобы она могла выполняться даже при неверно заданных входных данных и выдавать полезную информацию об ошибках. 5.1 Совершенствование команды
cal
Типичная задача программирования на языке
shell
сводится к изменению взаимодействия между пользователем и программой, чтобы сделать это взаимодействие более удобным. В качестве примера рассмотрим команду cal(1)
:
$ cal
usage: cal [month] year
Пока хорошо
$ cal october
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
выдает календарь на весь 10-й год, а не на октябрь текущего года. Поэтому всегда следует указывать год, если вы хотите получить календарь на один месяц. Указанные выше неудобства связаны с тем, что взаимодействие пользователя с программой было реализовано без привлечения команды
cal
. Вы можете изменить характер этого взаимодействия, не меняя самой программы. Если поместить команду в ваш собственный каталог bin
, то возможен более удобный способ перевода аргументов в те, которые нужны настоящей команде cal
. Вы можете даже вызывать свою версию команды, и тогда вам меньше придется запоминать. Первый шаг разработки — определить функции усовершенствованной команды
cal
. В основном мы хотим от нее разумного поведения. Месяц нужно распознавать по названию. При наличии двух аргументов она должна делать то же, что делала прежняя версия, за исключением перевода названия месяца в его номер. В случае одного аргумента следует печатать календарь месяца или года (в зависимости от того, что вам требуется), а при отсутствии аргументов — календарь текущего месяца, так как большей частью именно для этого и обращаются к команде. Поэтому задача сводится к тому, чтобы определить, сколько аргументов задано, и преобразовать их в те параметры, которые требуются стандартной команде cal
. Язык
shell
имеет оператор case
, который успешно применяется в таких ситуациях:
case слово in
шаблон) команды ;;
шаблон) команды ;;
...
esac
В операторе
case
слово сравнивается поочередно со всеми шаблонами от начала до конца и выполняются команды, связанные с первым (и только первым) шаблоном, соответствующим слову. Шаблоны составляются по правилам соответствия шаблонов, которые в некоторой степени обобщают правила задания имен файлов. Каждое действие завершается двумя символами ;;
(для последнего варианта можно обойтись без ;;
, но обычно мы ставим их для удобства редактирования). В нашей версии команды определяется число заданных аргументов, обрабатываются названия месяцев, затем происходит обращение к настоящей команде
cal
. В переменной интерпретатора $#
хранится число аргументов, с которыми была вызвана программа; другие специальные переменные интерпретатора перечислены в табл. 5.1. $# | Число аргументов |
$* | Все аргументы, передаваемые интерпретатору |
$@ | Аналогично $* ; см. разд. 5.7 |
$- | Флаги, передаваемые интерпретатору |
$? | Возвращение значения последней выполненной команды |
$$ | Номер процесса интерпретатора |
$! | Номер процесса последней команды, запущенной с помощью & |
$НOМЕ | Аргумент, принятый по умолчанию для команды cd |
$IFS | Список символов, разделяющих слова в аргументах |
$MAIL | Файл, изменение которого приводит к появлению сообщения "you have a mail" ("У вас есть почта") |
$PATH | Список каталогов, в которых осуществляется поиск команд |
$PS1 | Строка приглашение, по умолчанию принята '$' |
$PS2 | Строка приглашение при продолжении командной строки, по умолчанию принята '>' |
Таблица 5.1: Встроенные переменные интерпретатора
$ cat cal
# cal: nicer interface to /usr/bin/cal
case $# in
0) set `date`; m=$2; y=$6 ;; # no args: use today
1) m=$l; set `date`; y=$6 ;; #1 arg: use this year
*) m=$1; y=$2 ;; #2 args: month and year
esac
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) ;; # numeric month