Введение в UNIX
Дегтярев Е.К.
Оглавление
7. Программирование в языке SHELL
В начало страницы
Shell - интерпретатор команд, подаваемых с терминала или
из командного файла. Это обычная программа (т.е. не входит в
ядро операционной системы UNIX). Ее можно заменить на другую
или иметь несколько.
Две наиболее известные версии:
- Shell (версии 7 UNIX) или Bourne Shell (от фамилии ав-
тора S.R.Bourne из фирмы Bell Labs) [5];
- C-Shell (версии Berkley UNIX).
Они похожи, но есть и отличия: C-Shell мощнее в диалого-
вом режиме, а обычный Shell имеет более элегантные управляю-
щие структуры.
Shell - язык программирования, так как имеет:
- переменные;
- управляющие структуры (типа if);
- подпрограммы (в том числе командные файлы);
- передачу параметров;
- обработку прерываний.
В начало страницы
Независимо от версии Shell при входе в систему UNIX ищет
файл начала сеанса с предопределенным именем, чтобы выпол-
нить его как командный файл;
- для UNIX версии 7 это: .profile;
- для C-Shell это: .login и/или .cshrc.
В этот файл обычно помещают команды:
- установки характеристик терминала;
- оповещения типа who, date;
- установки каталогов поиска команд (обычно: /bin, /usr/bin);
- смена подсказки с $ на другой символ и т.д.
В начало страницы
Это командный файл. Два способа его вызова на выполнение:
1. $ sh dothat (где dothat - некоторый командный файл);
2. $ chmod 755 dothat (сделать его выполнимым, т.е.
-rwxr-xr-x)
$ dothat.
Следует знать порядок поиска каталогов команд (по умолча-
нию):
- текущий;
- системный /bin;
- системный /usr/bin.
Следовательно, если имя вашего командного файла дублирует
имя команды в системных каталогах, последняя станет недос-
тупной (если только не набирать ее полного имени).
В начало страницы
В языке Shell версии 7 определение переменной содержит
имя и значение: var = value.
Доступ к переменной - по имени со знаком $ спереди:
fruit = apple (определение);
echo $fruit (доступ);
apple (результат echo).
Таким образом, переменная - это строка. Возможна конкате-
кация строк:
$ fruit = apple
$ fruit = pine$fruit
$ echo $fruit
pineapple
$ fruite = apple
$ wine = ${fruite}jack
$ echo $wine
applejack
$
Другие способы установки значения переменной - ввод из
файла или вывод из команды (см. раздел 7.6), а также присва-
ивание значений переменной - параметру цикла for из списка
значений, заданного явно или по умолчанию (см. раздел 7.9).
Переменная может быть:
1) Частью полного имени файла: $d/filename, где $d - пе-
ременная (например, d = /usr/bin).
2) Частью команды:
$ S = "sort + 2n + 1 - 2" (наличие пробелов требует кавы-
чек "")
$ $S tennis/lpr
$ $S basketball/lpr
$ $S pingpong/lpr
$
Однако внутри значения для команды не могут быть символы
|, >, <, & (обозначающие канал, перенаправления и фоновый
режим).
В начало страницы
Некоторые из них можно только читать. Наиболее употреби-
тельные:
HOME - "домашний" каталог пользователя; служит аргументом
по умолчанию для cd;
PATH - множество каталогов, в которых UNIX ищет команды;
PS1 - первичная подсказка (строка) системы (для v.7 - $).
Изменение PS1 (подсказки) обычно делается в login - фай-
ле, например:
PS1 = ?
или PS1 = "? " (с пробелом, что удобнее).
Изменение PATH:
$ echo $PATH - посмотреть;
:/bin:/usr/bin - значение PATH;
$ cd - "домой";
$ mkdir bin - новый каталог;
$ echo $HOME - посмотреть;
/users/maryann - текущий каталог;
$ PATH = :$HOME/bin:$PATH - изменение PATH;
$ echo $PATH - посмотреть;
:/users/maryann/bin:/bin:/usr/bin - новое значение PATH.
В начало страницы
Пример 1:
$ now = `date` (где `` - обратные кавычки)
$ echo $now
Sun Feb 14 12:00:01 PST 1985
$
Пример 2: (получение значения переменной из файла):
$ menu = `cat food`
$ echo $menu
apples cheddar chardonnay (символы возврата каретки за-
меняются на пробелы).
В начало страницы
Это особый тип переменных, именуемых цифрами.
Пример: $ dothis grapes apples pears (процедура).
Тогда позиционные параметры (аргументы) этой команды дос-
тупны по именам:
$1 = `grapes`
$2 = `apples`
$3 = `pears`
и т.д. до $9. Однако есть команда shift, которая сдвигает
имена на остальные аргументы, если их больше 9 (окно шириной
9).
Другой способ получить все аргументы (даже если их больше
9):
$*, что эквивалентно $1$2 ...
Количество аргументов присваивается другой переменной:
$#(диез). Наконец, имя процедуры - это $0; переменная $0 не
учитывается при подсчете $#.
В начало страницы
Кроме процедур, в языке Shell имеются структурные опера-
торы типа "if-else" и "while-do". Программирование на Shell,
кроме написания процедур, используется для:
- отработки алгоритма перед кодированием его в языках С
или ФОРТРАН-77 (нет компиляции, линкирования, загрузки,
простота отладки);
- обучения принципам программирования непрограммистов.
Пусть имеется командный файл makelist (процедура)
$ cat makelist
sort +1 -2 people | tr -d -9 | pr -h Distribution | lpr.
Если вместо одного файла people имеется несколько, нап-
ример:
adminpeople, hardpeople, softpeople,...,
то необходимо повторить выполнение процедуры с различными
файлами. Это возможно с помощью for - оператора.
Синтаксис:
for <переменная> in <список значений>
do <список команд>
done
Ключевые слова for, do, done пишутся с начала строки.
Пример (изменим процедуру makelist)
for file in adminpeople, hardpeople, softpeople
do
Sort +1 -2 $file | tr ... | lpr
done.
Можно использовать метасимволы Shell в списке значений.
Пример:
for file in *people (для всех имен, кончающихся на people)
do
...
done.
Если in опущено, то по умолчанию в качестве списка значе-
ний берется список аргументов процедуры, в которой содержит-
ся цикл, а если цикл не в процедуре, то - список параметров
командной строки (то есть в качестве процедуры выступает ко-
манда).
Пример: for file
do
...
done
Для вызова makelist adminpeople hardpeople softpeople бу-
дет сделано то же самое.
В начало страницы
Используем имена переменных, представляющие значения па-
раметров процедуры:
sort +1 -2 $1 | tr ... | lpr
Пример неверного вызова:
makelist (без параметров), где $1 неопределен.
Исправить ошибку можно, проверяя количество аргументов -
значение переменной $# посредством if - оператора.
Пример: (измененной процедуры makelist):
if test $# -eq 0
then echo "You must give a filename"
exit 1
else sort +1 -2 $1 | tr ... | lpr
fi
Здесь test и exit - команды проверки (см. раздел 7.11) и
выхода.
Таким образом, синтаксис оператора if:
if <если эта команда выполняется успешно, то>;
then <выполнить все следующие команды до else или, если
его нет, до fi>;
[else <иначе выполнить следующие команды до fi>]
Ключевые слова if, then, else и fi пишутся с начала строки.
Успешное выполнение процедуры означает, что она возвраща-
ет значение true = 0 (zero) (неуспех - возвращаемое значение
не равно 0).
Оператор exit 1 задает возвращаемое значение 1 для неу-
дачного выполнения makelist и завершает процедуру.
Возможны вложенные if. Для else if есть сокращение elif,
которое одновременно сокращает fi.
В начало страницы
Не является частью Shell, но применяется внутри Shell-
процедур.
Имеется три типа проверок:
- оценка числовых значений;
- оценка типа файла;
- оценка строк.
Для каждого типа свои примитивы (операции op).
Для чисел синтаксис такой:
N op M, где N, M - числа или числовые переменные;
op принимает значения: -eq, -ne, gt, -lt, -ge, -le (с
обычным смыслом, как, например, в ФОРТРАН).
Для файла синтаксис такой:
op filename,
где op принимает значения:
-s (файл существует и не пуст);
-f (файл, а не каталог);
-d (файл-директория (каталог);
-w (файл для записи);
-r (файл для чтения).
Для строк синтаксис такой:
S op R, где S, R - строки или строковые переменные
или op1 S
op принимает значения:
= (эквивалентность);
!= (не эквивалентность);
op1 принимает значения:
-z (строка нулевой длины);
-n (не нулевая длина строки).
Наконец, несколько проверок разных типов могут быть объ-
единены логическими операциями
-a (AND) и -o (OR).
Примеры:
$ if test -w $2 -a -r S1
> then cat $1 >> $2
> else echo "cannot append"
> fi
$
В некоторых вариантах ОС UNIX вместо команды test исполь-
зуются квадратные скобки, т.е. if [...] вместо if test
... .
В начало страницы
Синтаксис:
while <команда>
do
<команды>
done
Если "команда" выполняется успешно, то выполнить "коман-
ды", завершаемые ключевым словом done.
Пример:
if test $# -eq 0
then echo "Usage: $0 file ..." > &2
exit
fi
while test $# -gt 0
do if test -s $1
then echo "no file $1" > &2
else sort + 1 - 2 $1 | tr -d ... (процедуры)
fi
shift (* перенумеровать аргументы *)
done
Процедуры выполняются над всеми аргументами.
В начало страницы
Инвертирует условие повторения по сравнению с while
Синтаксис:
until <команда>
do
<команды>
done
Пока "команда" не выполнится успешно, выполнять команды,
завершаемые словом done.
Пример:
if test S# -eq 0
then echo "Usage $0 file..." > &2
exit
fi
until test S# -eq 0
do
if test -s $1
then echo "no file $1" > &2
else sort +1 -2 $1 | tr -d ... (процедура)
fi
shift (сдвиг аргументов)
done
Исполняется аналогично предыдущему.
В начало страницы
Синтаксис:
case in
string1) <если string = string1, то выполнить все следую-
щие команды до ;; > ;;
string2) <если string = string2, то выполнить все следую-
щие команды до ;; > ;;
string3) ... и т.д. ...
esac
Пример:
Пусть процедура имеет опцию -t, которая может быть подана
как первый параметр:
.................
together = no
case $1 in
-t) together = yes
shift ;;
-?) echo "$0: no option $1"
exit ;;
esac
if test $together = yes
then sort ...
fi
где ? - метасимвол (если -?, т.е. "другая" опция, отлич-
ная от -t, то ошибка). Можно употреблять все метасимволы
языка Shell, включая ?, *, [-].
Легко добавить (в примере) другие опции, просто расширяя
case.
В начало страницы
Это специальный каталог, в котором все файлы доступны на
запись всем пользователям.
Если некоторая процедура, создающая временный файл, ис-
пользуется несколькими пользователями, то необходимо обеспе-
чить уникальность имен создаваемых файлов. Стандартный прием
- имя временного файла $0$$, где $0 - имя процедуры, а $$ -
стандартная переменная, равная уникальному идентификационно-
му номеру процесса, выполняющего текущую команду.
Хотя администратор периодически удаляет временные файлы в
/tmp, хорошей практикой является их явное удаление после ис-
пользования.
Они начинаются с двоеточия :, которое считается нуль-ко-
мандой, а текст комментария - ее аргументом. Чтобы Shell не
интерпретировал метасимволы ($, * и т.д.), рекомендуется
заключать текст комментария в одиночные кавычки.
В некоторых вариантах ОС UNIX примечание начинается со
знака #.
В начало страницы
:'Эта процедура работает с файлами, содержащими имена'
: 'и номера телефонов,'
:'сортирует их вместе или порознь и печатает результат на'
:'экране или на принтере'
:'Ключи процедуры:'
:'-t (together) - слить и сортировать все файлы вместе'
:'-p (printer) - печатать файлы на принтере'
if test $# - eq 0
then echo "Usage: $ 0 file ... " > & 2
exit
fi
together = no
print = no
while test $# -gt 0
do case $1 in
-t) together = yes
shift ;;
-p) print = yes
shift ;;
-?) echo "$0: no option $1"
exit ;;
*) if test $together = yes
then sort -u +1 -2 $1 | tr ... > /tmp/$0$$
if $print = no
then cat /tmp/$0$$
else lpr -c /tmp/$0$$
fi
rm /tmp/$0$$
exit
else if test -s $1
then echo "no file $1" > &2
else sort +1 -2 $1 | tr...> /tmp/$0$$
if $print = no
then cat /tmp/$0$$
else lpr -c /tmp/$0$$
fi
rm /tmp/$0$$
fi
shift
fi;;
esac
done.
Процедура проверяет число параметров $#, и если оно равно
нулю, завершается. В противном случае она обрабатывает пара-
метры (оператор case). В качестве параметра может выступать
либо ключ (символ, предваряемый минусом), либо имя файла
(строка, представленная метасимволом *). Если ключ отличен
от допустимого (метасимвол ? отличен от t и p), процедура
завершается. Иначе в зависимости от наличия ключей t и p вы-
полняются действия, заявленные в комментарии в начале проце-
дуры.
В начало страницы
Если при выполнении процедуры получен сигнал прерывания
(от клавиши BREAK или DEL, например), то все созданные вре-
менные файлы останутся неудаленными (пока это не сделает ад-
министратор) ввиду немедленного прекращения процесса.
Лучшим решением является обработка прерываний внутри про-
цедуры оператором trap:
Синтаксис: trap 'command arguments' signals...
Кавычки формируют первый аргумент из нескольких команд,
разделенных точкой с запятой. Они будут выполнены, если воз-
никнет прерывание, указанное аргументами signals (целые):
2 - когда вы прерываете процесс;
1 - если вы "зависли" (отключены от системы)
и др.
Пример (развитие предыдущего):
case $1 in
.....
*) trap 'rm /tmp/*; exit' 2 1 (удаление временных файлов)
if test -s $1
..............
rm /tmp/*
Лучше было бы:
trap 'rm /tmp/* > /dev/null; exit' 2 1
так как прерывание может случиться до того, как файл
/tmp/$0$$ создан и аварийное сообщение об этом случае пере-
направляется на null-устройство.
В начало страницы
Команда expr вычисляет значение выражения, поданного в
качестве аргумента и посылает результат на стандартный вы-
вод. Наиболее интересным применением является выполнение
операций над переменными языка Shell.
Пример суммирования 3 чисел:
$ cat sum3
expr $1 + $2 + $3
$ chmod 755 sum3
$ sum3 13 49 2
64
$
Пример непосредственного использования команды:
$ expr 13 + 49 + 2 + 64 + 1
129
$
В expr можно применять следующие арифметические операто-
ры: +, -, *, /, % (остаток). Все операнды и операции должны
быть разделены пробелами.
Заметим, что знак умножения следует заключать в кавычки
(одинарные или двойные), например: '*', так как символ *
имеет в Shell специальный смысл.
Более сложный пример expr в процедуре (фрагмент):
num = 'wc -l < $1'
tot = 100
count = $num
avint = 'expr $tot / $num'
avdec = 'expr $tot % $num'
while test $count -gt 0
do ...
Здесь wc -l осуществляет подсчет числа строк в файле, а да-
лее это число используется в выражениях.
В начало страницы
Имеются три средства, позволяющие вести отладку процедур.
1) Размещение в теле процедуры команд echo для выдачи со-
общений, являющихся трассой выполнения процедуры.
2) Опция -v (verbose = многословный) в команде Shell при-
водит к печати команды на экране перед ее выполнением.
3) Опция -x (execute) в команде Shell приводит к печати
команды на экране по мере ее выполнения с заменой всех пере-
менных их значениями; это наиболее мощное средство.
|