Пользователь ОС UNIX общается с системой через командный интерпретатор (shell). Через него происходит доступ к командам, файловой системе и другим функциям ядра UNIX. Это обычная программа (т.е. не входит в ядро операционной системы UNIX). Ее можно заменить другой или использовать несколько разных версий. Наиболее известны следующие версии:
В рамках этого курса мы будем рассматривать, в основном, средства, не выходящие за пределы возможностей командных интерпретаторов sh и ksh (в классической версии 1988 года).
Командные строки рассматриваются по одной и имеют определенную структуру. Чтобы понять ее, рассмотрим ряд синтаксических определений:
Итак, простая команда - это последовательность слов через пробел. Нажатие клавиши Enter при вводе команды или перевод строки при обработке сценария являются для командного интерпретатора признаком завершения команды. Она обрабатывается и выполняется.
Значением простой команды является ее статус выхода (см. далее) в случае нормального завершения или (восьмеричное) 200+статус при ненормальном завершении.
Пример простой команды:
$ who oracle pts000 Aug 20 10:08 root console Aug 20 09:03 intdbi pts004 Aug 20 12:45 $
Из простых команд строятся более сложные конструкции: конвейеры и списки.
Конвейер - это последовательность одной или более команд, разделенных |. Стандартный выходной поток каждой команды, кроме последней, соединяется при помощи программного канала со стандартным входным потоком следующей команды. Каждая команда выполняется как отдельный процесс; интерпретатор ожидает окончания последней команды. Статусом выхода конвейера является статус выхода его последней команды. Вот пример простого конвейера:
$ ls | tee save | wc
15 15 100
$
Список - это последовательность одного или более конвейеров, разделенных ;, &, && или || и, возможно, заканчивающаяся ; или &. Из этих четырех символов, ; и & имеют равный приоритет, который ниже, чем у && и || (эти символы тоже имеют равный приоритет). Точка с запятой (;) вызывает последовательное выполнение предшествующего конвейера (т.е. командный интерпретатор ожидает окончания конвейера перед выполнением любых команд, следующих за точкой с запятой). Амперсанд (&) вызывает асинхронное выполнение предшествующего конвейера (т.е. командный интерпретатор не ожидает окончания работы конвейера). Символ && (||) ведет к тому, что следующий за ним список выполняется только в том случае, когда предыдущий конвейер вернул нулевой (ненулевой) статус выхода. В список может входить произвольное количество переводов строк и точек с запятой, разделяющих команды.
Теперь можно дать общее определение команды:
Список в круглых скобках выполняется в порожденном командном интерпретаторе. Круглые скобки обычно используют для группировки команд.
Список в фигурных скобках выполняется в текущем командном интерпретаторе, без порождения дополнительного процесса, и замещает образ командного интерпретатора (это аналог системного вызова exec).
Операторы управления и синтаксис определения функций рассматривается далее.
Рассмотрим пример сложной команды:
bash$ (sleep 5; date) & date [1] 1148 Wed Aug 20 15:00:11 ???? 1997 bash$ Wed Aug 20 15:00:16 ???? 1997
Фоновый процесс начинается, но сразу "засыпает"; тем временем вторая команда date выдает текущее время, а интерпретатор - приглашение для ввода новой команды. Через пять (примерно, зависит от загрузки системы и т.п.) секунд прекращается выполнение команды sleep и первая команда date выдает новое время.
Ряд символов, как было описано выше, имеют для командного интерпретатора специальное значение - это метасимволы. Они описаны в табл. 23.
Метасимволы не входят в команды и обрабатываются в несколько проходов до начала выполнения реальных программ.
Таблица 23. Метасимволы командного интерпретатора
| Метасимвол | Интерпретация |
| > | prog>file - переключить стандартный выходной поток в файл |
| >> | prog>>file - добавить стандартный выходной поток к файлу |
| < | prog<file - извлечь стандартный входной поток из файла |
| | | p1 | p2 - передать стандартный выходной поток p1 как стандартный входной поток p2 |
| <<str | "Документ здесь": стандартный входной поток задается в последующих строках до строки, состоящей только из символов str. По умолчанию в данных интерпретируются метасимволы \, $ и ``. Если необходимо предотвратить в данных интерпретацию всех метасимволов, необходимо экранировать строку str, предварив обратной косой или взяв в двойные или одиночные кавычки. |
| * | Задает в имени файла любую строку из нуля или более символов |
| ? | Задает любой символ в имени файла |
| [abc] | Задает любой символ из [abc] в имени файла, при этом допускаются диапазоны, задаваемые при помощи дефиса -. Если первым символом после [ является !, с этой конструкцией сопоставляется любой символ, не входящий в квадратные скобки. |
| ; | Разделитель команд: p1; p2 - выполнить p1, затем p2. |
| & | |
| `...` | Инициирует выполнение команд(ы) в ...; `...` заменяется на полученный в результате выполнения стандартный выходной поток |
| (...) | Инициирует выполнение команд(ы) ... в порожденном командном интерпретаторе |
| $1,$2,...$9 | Заменяются аргументами командного файла |
| $var | Значение переменной (ключевого параметра) var в сеансе |
| ${var} | Значение var: исключает коллизии в случае конкатенации переменной с последующим текстом |
| \ | \c - использовать непосредственно символ c, \перевод строки - отбрасывается |
| '...' | Непосредственное использование того, что в кавычках |
| "..." | Непосредственное использование, но после того, как будут интерпретированы метасимволы $, `...` и \ |
| # | Начало комментария |
| var=value | Присваивает значение value переменной var |
| p1&&p2 | |
| p1||p2 | Выполнить p1; в случае неудачи выполнить p2 |
Перед выполнением команды каждое слово-аргумент команды просматривается в поисках метасимволов *, ? и [. Если в слове есть один из этих символов, слово рассматривается как шаблон (обратите внимание на синтаксические отличия от шаблонов ed). Такое слово заменяется отсортированными в алфавитном порядке именами файлов, соответствующих шаблону. Если ни одно их имен файлов не соответствует шаблону, слово оставляется без изменений. Символ . в начале имени файла или сразу после /, а также сам символ /, должны сопоставляться явно.
При таком количестве метасимволов интерпретатора необходимо иметь возможность экранировать специальный символ от интерпретации. Для этого можно использовать апострофы, кавычки или обратную косую. При этом кавычки одного вида могут экранировать кавычки другого вида. Обратите внимание, что кавычки "", в отличие от апострофов, не экранируют строку полностью - интерпретатор заглядывает внутрь кавычек в поисках $, `...` и \.
В кавычках могут содержаться переводы строк, пробелы, табуляции, символы ;, &, (, ), |, ^, < и >. Задавая имя файла в кавычках, можно создать файлы с такими нетривиальными символами в именах. Впрочем, делать это не рекомендуется, так как работать с ними будет явно неудобно.
Имея последовательность команд, которую придется многократно использовать, преобразуем ее для удобства в "новую" команду со своим именем и будем использовать ее как обычную команду. Предположим, что вам как администратору предстоит часто подсчитывать количество пользователей, работающих в настоящий момент в системе, при помощи простого конвейера
$ who | wc -l
и для этой цели нужна новая программа nu.
Первым шагом будет создание обычного текстового файла, содержащего текст конвейера. Можно воспользоваться вашим любимым текстовым редактором, а можно проявить изобретательность:
$ echo 'who | wc -l' >nu
Интерпретатор является такой же программой, как редактор, who или wc. Раз это программа, ее можно вызвать и переключить ее входной поток. Итак, запускаем интерпретатор с входным потоком, поступающим из файла nu, а не с терминала:
$ who
oracle pts000 Aug 20 10:08
root console Aug 20 09:03
intdbi pts004 Aug 20 12:45
$ cat nu
who | wc -l
$ sh < nu
3
$
Результат получился таким же, как и при вводе команды с терминала. Как и многие другие программы, интерпретатор обрабатывает файл, если он указан в качестве аргумента; вы с тем же успехом могли бы задать:
$ sh nu
На самом деле, этот вызов отличается, так как входной поток sh остается связанным с терминалом.
Не хотелось бы вводить sh каждый раз, кроме того, это создает различие между командами, написанными на языке shell, и другими выполняемыми файлами. Поэтому, если текстовый файл предназначен для выполнения, то интерпретатор считает, что он состоит из команд (интерпретатор csh требует, чтобы файл начинался с #).
#!/полный_путь_к_программе опции_программыто данный текстовый файл будет интерпретироваться указанной программой, при вызове которой будут установлены заданные опции. Так можно выполнять, например, программы командного интерпретатора csh, не выходя из sh. Точно так же можно автоматически вызвать и интерпретаторы других языков сценариев, например, Perl.
Все, что вам нужно сделать, это объявить файл nu выполняемым, задав:
$ chmod +x nu
а затем вы можете вызывать его посредством
$ nu
На самом деле, при выполнении команды nu создается новый процесс интерпретатора (порожденный интерпретатор), который и выполняет команды, содержащиеся в nu. Чтобы команда выполнялась в том же интерпретаторе, необходимо поставить перед вызовом команды точку (.). Заметьте, что
$ . nu
выполняется быстрее, чем простой вызов nu.
В большинстве программ надо использовать аргументы - файлы, флаги и т.д. В соответствии с синтаксисом командной строки, это можно сделать, передавая их после имени команды через пробелы.
Предположим, необходимо программу cx для установки права доступа к файлу на выполнение, так что
$ cx nu
есть сокращенная запись для
$ chmod +x nu
Создать такой сценарий не сложно. Остается только один вопрос - как получить в программе доступ к имени файла-аргумента. Для этого в командном интерпретаторе используются позиционные параметры.
При выполнении командного файла, каждое вхождение $1 заменяется первым аргументом, $2 - вторым и так далее до $9. $0 заменяется именем выполняемой команды. Поэтому, если файл cx содержит строку
chmod +x $1
то при выполнении команды
$ cx nu
порожденный интерпретатор заменит $1 первым аргументом команды nu.
Значения позиционным параметрам присваиваются при вызове сценария, при вызове функции в сценарии или явно, с помощью команды set.
Как быть, если нужно работать с несколькими аргументами, например, заставить программу cx делать выполняемыми несколько файлов? Можно включить девять аргументов в командный файл (явно можно задавать только девять аргументов, так как конструкция $10 распознается как "первый аргумент, за которым следует 0"):
chmod +x $1 $2 $3 $4 $5 $6 $7 $8 $9
Если пользователь такого командного файла задаст менее девяти аргументов, то оставшиеся окажутся пустыми строками и только настоящие аргументы будут переданы chmod порожденным интерпретатором. Но такое решение не подходит, если количество аргументов превышает девять.
Интерпретатор предоставляет специальный параметр $*, который заменяется всеми аргументами команды, независимо от их количества. С учетом этого, правильное определение cx будет таким:
chmod +x $*
Все позиционные и специальные параметры, поддерживаемые командным интерпретатором, представлены в табл. 24.
Таблица 24. Позиционные и специальные параметры командного интерпретатора
| Параметр | Назначение |
| $0 | Имя выполняемой команды |
| $1,$2,...$9 | Заменяются аргументами командного файла |
| $# | Количество аргументов |
| $* | Все аргументы, передаваемые интерпретатору. "$*" является единым словом, образованным из всех аргументов, объединенных вместе с пробелами. |
| $@ | Аналогично $*. "$@" идентично аргументам: пробелы в аргументах игнорируются, и получается список слов, идентичных исходным аргументам. |
| $- | Флаги, установленные в интерпретаторе. |
| $? | Значение, возвращенное последней выполненной командой (статус выхода). |
| $$ | Номер процесса интерпретатора. |
| $! | Номер процесса последней команды, запущенной асинхронно с помощью &. |
Подобно большинству языков программирования, командный интерпретатор имеет переменные, которые называют еще ключевыми параметрами (поскольку они предаются по имени - ключу).
Переменные можно создавать, изменять и выбирать их значения. Для присваивания значения переменной применяется конструкция:
переменная=значение
Обратите внимание на отсутствие пробелов до и после знака присваивания. Вспомните, что командный интерпретатор считает пробелы разделителями слов. Если поставить пробел после знака присваивания, то интерпретатор не изменит значения переменной, но будет искать команду с именем значение.
Присваиваемое значение должно выражаться одним словом, и его следует взять в кавычки, если оно содержит метасимволы, которые не нужно обрабатывать.
Создание (определение) переменной происходит при первом присваивании ей значения. Переменные не нужно явно объявлять до момента их использования. Если переменная не объявлена (не получила значения), при обращении к ней будет получена пустая строка. Все переменные в командном интерпретаторе - строковые, поэтому тип переменной задавать не надо. Некоторые команды интерпретируют строки как числа.
Многие переменные, как, например, PATH, имеют специальное значение для интерпретатора. По традиции, такие переменные называют встроенными и обозначают прописными буквами, а обычные переменные - строчными. Основные встроенные переменные представлены в табл. 25.
Таблица 25. Встроенные переменные командного интерпретатора
| Переменная | Значение |
| $HOME | Начальный каталог пользователя. |
| $PATH | Путь для поиска выполняемых команд. |
| $CDPATH | Путь поиска для команды cd. |
| $IFS | Список символов, разделяющих слова в аргументах |
| Файл почтового ящика. Командный интерпретатор информирует пользователя о получении почты в указанный файл. | |
| $MAILCHECK | Эта переменная определяет, как часто (в секундах) интерпретатор будет проверять поступление почты в файл, определяемый переменной MAIL. По умолчанию принято значение 600 секунд. При установке в 0, интерпретатор будет проверять почту перед каждой выдачей строки-приглашения. |
| $PS1 | Строка-приглашение, по умолчанию принята '$ ' |
| $PS2 | Строка-приглашение при продолжении командной строки, по умолчанию принята '> ' |
Типичным примером использования переменных является хранение в них длинных строк, таких как имена файлов:
$ pwd /home/intdbi/dosapps/doc/book/unix/shell $ dir=`pwd` $ cd $ ln $dir/cx . ... $ cd $dir $ pwd /home/intdbi/dosapps/doc/book/unix/shell
Встроенная команда интерпретатора set, при вызове без параметров показывает значение всех определенных переменных.
Переменные называют ключевыми параметрами, поскольку им можно передавать значение по имени при вызове. Рассмотрим пример:
$ echo 'echo $x' >echox $ cx echox $ echo $x Hello $ echox $ x=Hi echox Hi
По умолчанию ключевые параметры можно передавать только до имени команды. Если установить флаг интерпретатора -k (set -k), то можно будет передавать значения переменным и после имени команды.
Каждый экземпляр командного интерпретатора имеет свой набор переменных, размещаемых в отдельной области памяти. Если необходимо, чтобы определенная переменная в порожденных процессах имела конкретное значение, необходимо экспортировать ее в среду. Такая переменная называется переменной среды.
Для всех экспортированных переменных при запуске порожденного процесса создаются их локальные копии с теми же значениями. Рассмотрим пример:
$ x=Hello $ export x $ PS1='new$ ' sh new$ echo $x Hello new$ x='Good Bye' new$ echo $x Good Bye new$ exit $ $ echo $x Hello$
Изменение значение переменной в порожденном интерпретаторе не влияет на ее значение в родительском интерпретаторе.
Для просмотра значений всех переменных среды предназначена команда env.
Командный интерпретатор поддерживает циклическую обработку. Чаще всего на практике используется цикл for - цикл по списку слов. Он описан в следующем подразделе.
Обратите внимание, что выделенные полужирным ключевые слова должны быть первым словом команды, т.е. первым словом в строке или идти сразу после точки с запятой.
Цикл for имеет следующий синтаксис:
Переменная последовательно получает значение очередного слова из списка, и для этого значения выполняются команды в теле цикла. Цикл завершается, когда пройден весь список слов. По умолчанию в качестве списка слов используются аргументы командной строки.
Рассмотрим пару примеров таких циклов:
$ for i in 1 2 3 4 5 > do > echo $i > done
Обратите внимание, что командный интерпретатор распознает цикл, выдает вторичное приглашение, и выполняет цикл только после его завершения ключевым словом done.
Список слов для цикла обычно порождается динамически. Например, путем раскрытия шаблонов имен файлов:
$ for i in *.c *.h > do > echo $i > diff -b old/$i $i > echo > done | pr -h "diff `pwd`/old `pwd`" | lp & [4] 1430
Можно также порождать его командой, подставляя ее результаты:
$ for i in `pick *.c *.h` > do > echo $i: > diff -b old/$i $i > done | pr | lp
Командный интерпретатор поддерживает также традиционные циклы по условию со следующим синтаксисом:
Выполняются команды, задающие условие, и проверяется код возврата последней из них. Если это ноль (истина), выполняются команды в теле цикла while или завершается выполнение цикла until. Если это не ноль (ложь), завершается работа цикла while или выполняется очередная итерация цикла until.
На основе этих циклов часто создаются программы-"следилки", работающие бесконечно:
$ cat watchfor
# watchfor: watching for log ins and log outs...
PATH=/usr/bin
new=/tmp/wfor1.$$
old=/tmp/wfor2.$$
>$old # создает пустой файл
while : # бесконечный цикл
do
who >$new
diff $old $new
mv $new $old
sleep 60
done | awk ' />/ { $1 = "in: "; print }
/</ { $1 = "out: "; print }'
$
Командный интерпретатор поддерживает выполнение того или иного блока команд в зависимости от значения некоторого слова. Для этого предлагается оператор case со следующим синтаксисом:
Слово (обычно - значение переменной) сравнивается последовательно с шаблонами. Если произошло сопоставление (по правилам сопоставления шаблонов имен файлов) выполняются команды, соответствующие данному варианту и оператор завершается. Учтите, что шаблон *) сопоставляется с любым словом, и, тем самым, задает вариант по умолчанию.
В шаблонах оператора case символы . и /, в отличие от шаблонов имен файлов, не обязательно задавать явно.
Командный интерпретатор поддерживает условный оператор следующего общего вида:
Выполняются команды после if и проверяется код возврата последней из них. Если это 0 (истина) выполняются соответствующие команды после then и выполнение оператора завершается. Если же это не 0 (ложь), то при наличии конструкций elif выполняются последовательно соответствующие команды-условия и, если они возвращают код 0, команды после then, а затем оператор завершается. Если ни одно из условий не было истинным, выполняются команды в части else и оператор завершается.
В качестве условия в условном операторе может использоваться любая команда. Однако, имеется стандартная команда для проверки условий в традиционном понимании. Это команда test, представленная в следующем разделе.
Команда test имеет следующий синтаксис:
Выражение строится из примитивов, представленных в табл. 26, при необходимости, с помощью следующих операторов:
| ! | Унарный оператор отрицания. |
| -a | Бинарный оператор "и". |
| -o | Бинарный оператор "или". |
| (<выражение>) | Скобки для группировки. Учтите, что скобки распознаются командным интерпретатором, поэтому их надо брать в кавычки. |
Таблица 26. Основные примитивы команды test
| Примитив | Условие |
| -r файл | файл существует и доступен для чтения |
| -w файл | файл существует и доступен для записи |
| -x файл | файл существует и является выполняемым |
| -f файл | истина, если файл существует и является обычным файлом (не каталогом) |
| -d файл | файл существует и является каталогом |
| -h файл | файл существует и является символьной связью |
| -s файл | файл существует и не пуст |
| -t [ дескриптор ] | истина, если открытый файл с указанным дескриптором (по умолчанию, 1) ассоциирован с терминалом |
| -z s1 | истина, если строка s1 имеет нулевую длину |
| -n s1 | истина, если строка s1 имеет ненулевую длину |
| s1 = s2 | истина, если строки s1 и s2 идентичны |
| s1 != s2 | истина, если строки s1 и s2 не совпадают |
| s1 | истина, если строка s1 непустая |
| n1 -eq n2 | сравнение целых чисел на равенство (=). Можно использовать также и другие сравнения: -ne (!=), -gt (>), -ge (>=), -lt (<) и -le (<=). |
Рассмотрим пример использования условного оператора и команды test:
$ cat which
# which cmd: Безопасная версия сценария для выдачи каталога,
# из которого будет вызываться выполняемая программа
opath=$PATH
PATH=/usr/bin
# Это гарантирует использование настоящих команд
# echo, sed и test в любом случае!
case $# in
0) echo 'Usage: which command' 1>&2; exit 2
esac
for i in `echo $opath | sed 's/^:/.:/
s/::/:.:/g
s/:$/:./
s/:/ /g'`
do
if test -x $i/$1
then
echo $i/$1
exit 0 # команда найдена
fi
done
exit 1 # не найдена
$ which sed
./sed
$ which which
./which
В программах командного интерпретатора можно перехватывать и обрабатывать сигналы. Для этого используется команда trap, устанавливающая с момента выполнения обработчик в виде последовательности команд (одним словом) для всех перечисленных сигналов. Эта команда имеет следующий синтаксис:
Рассмотрим пример реализации команды nohup, позволяющей запустить программу так, чтобы она продолжала работать при выключении терминала:
$ cat nohup # nohup: no kill and hangup trap "" 1 15 if test -t 2>&1 then echo "Redirect stdout to 'nohup.out'" exec nice -5 $* >>nohup.out 2>&1 else exec nice -5 $* 2>&1 fi $
Командный интерпретатор позволяет, при необходимости, запрашивать у пользователя информацию, которая помещается в указанную переменную. Для этого используется команда read:
$ read greeting Hello, world! $ echo $greeting Hello, world! $
На практике имеет смысл перед запросом выдать приглашение с помощью команды echo. Например, вот так:
$ cat pick # pick: select arguments PATH=/bin:/usr/bin for i # for each argument, try $*, "$*" and "$@" do echo -n "$i? " > /dev/tty read responce case $responce in y*) echo $i;; q*) break esac done </dev/tty $
Представленная выше программа pick выдает каждое указанное в качестве аргумента слово в отельной строке со знаком вопроса и требует от пользователя подтвердить необходимость его выдачи в стандартный выходной поток. Поскольку эта программа может использоваться в других сценариях, входной и выходной потоки которых перенаправлены, она взаимодействует непосредственно с текущим терминалом (через устройство /dev/tty).
Вычисления можно выполнять с помощью любой программы, воспринимающей свои параметры как выражение, значение которого необходимо вычислить, и выдающей результат вычисления в стандартный выходной поток. Одна из таких программ, expr, рассмотрена далее. Но современные командные интерпретаторы включают встроенную команду для выполнения простейших арифметических действий. Это команда let:
Вот как ее можно использовать:
$ let a=5 $ echo $a 5 $ let a=a*a+34/2 $ echo $a 42 $ let "a = 7" $ echo $a 7
Обратите внимание, что если вокруг знака равенства идут пробелы, необходимо брать выражение в кавычки. Команда let требует, чтобы выражение было одним словом. Кроме того, для обращения к значению переменной в этой команде не нужно использовать метасимвол $.
Одной из стандартных программ-калькуляторов является программа expr. Ее основные операторы представлены в табл. 27.
Таблица 27. Основные операторы, распознаваемые командой expr
| Оператор | Результат |
| выр1 \| выр2 | Возвращает значение первого выражения, если оно не пустое и не равно 0, иначе, возвращает значение второго выражения. |
| выр1 \& выр2 | Возвращает значение первого выражения, если оба выражения - не пустые и не равны 0, иначе, возвращает 0. |
| выр1 { +, - } выр2 | Складывает или вычитает целочисленные аргументы. |
| выр1 { \*, /, % } выр2 | Умножает, делит или возвращает остаток от деления для целочисленных аргументов. |
| length строка | Возвращает длину строки. |
Рассмотрим простой пример вычисления с помощью expr:
$ a=5 $ echo $a 5 $ a=`expr $a \* $a + 34 / 2` $ echo $a 42
Обратите внимание, что между элементами выражения надо указывать пробелы.
Стандартным способом разбиения программ на модули в командном интерпретаторе является оформление необходимых действий в виде отдельного выполняемого файла с программой командного интерпретатора - создание новой команды. Тем не менее, для некоторых модулей такой подход может оказаться неэффективным и избыточным, так как модули могут не представлять самостоятельного значения вне программы, в которой они используются. Поэтому в современных версиях командных интерпретаторов предлагается возможность создавать и вызывать функции.
Для определения функций используется ключевое слово function. Функции читаются и хранятся внутренне командным интерпретатором. Функции выполняются как команды, причем аргументы передаются как позиционные параметры. Синтаксис определения функции следующий:
где список команд задает команды, выполняемые в качестве тела функции. Команды обычно разделяются точкой с запятой или переводами строк.
Функции выполняются вызвавшим их процессом и используют все его файлы и текущий рабочий каталог. Сигналы, перехватываемые вызывающим процессом, внутри фунции обрабатываются стандартным образом. Сигналы, не перехватываемые или игнорируемые функцией, прекращают ее выполнение и передаются вызвавшей команде.
Обычно переменные совместно используются вызывающей программой и функцией. Однако, специальная команда typeset, используемая внутри функции, позволяет определять локальные переменные, область действия которых - текущая функция и все вызываемые ею функции.
Для выхода из функции используется специальная команда return. В случае ошибки в функции, управление передается вызывающей команде.
Идентификаторы определенных функций можно получить с помощью опций -f или +f специальной команды typeset. Текст функций показывается при использовании опции -f. Определение функции можно отменить с помощью опции -f специальной команды unset.
Обычно при выполнении сценария командным интерпретатором никакие функции не заданы. Опция -xf команды typeset позволяет экспортировать функцию для использования сценариями, выполняемыми без отдельного вызова интерпретатора. Функции, которые должны быть определены для всех вызовов интерпретатора, необходимо задавать в файле начального запуска с помощью опций -xf команды typeset.
Рассмотрим классический пример итеративной реализации функции вычисления факториала:
# test.sh - test shell functions
factorial () {
typeset i
typeset n
i=1; n=1
while [ $i -le $1 ]
do
let n=n*i
let i=i+1
done
echo $n
return
}
a=`factorial $11`
echo $a
При вызове эта программа, как и ожидалось, вычислит факториал своего первого параметра:
bash$ test.sh 5 120
Часто в виде функций оформляется выдача сообщений о параметрах вызова программы. В любом случае, если задача может быть разбита на подзадачи, решение этих подзадач имеет смысл оформлять в виде отдельной команды, если они полезны не только в контексте решаемой задачи, или в виде функции в противном случае.
Стандартная среда для работы командных интерпретаторов задается в файлах начального запуска, которые автоматически выполняются в начальном интерпретаторе с помощью команды точка (.), т.е. без порождения. Файлы начального запуска размещаются в начальном каталоге пользователя и называются .profile (sh, ksh) или .bash_profile (bash). Переменные, составляющие среду, в файле начального запуска надо экспортировать.
Рассмотрим пример содержимого файла начального запуска:
INFORMIXDIR=/usr/inf.731 INFORMIXSERVER=onarturo7 ONCONFIG=onconfig SQLHOSTS=sqlhosts PATH=$PATH:$INFORMIXDIR/bin LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$INFORMIXDIR/lib/esql:$INFORMIXDIR/lib DB_LOCALE=ru_ru.8859-5 CLIENT_LOCALE=ru_ru.8859-5 KAIOON=1 NODEFDAC=YES DBDATE=DMY4/ DBTIME='%H:%M:%S %d/%m/%Y' DBMONEY=.4 export KAIOON export INFORMIXDIR INFORMIXSERVER LANG PATH ONCONFIG SQLHOSTS export DBDATE DBTIME DBMONEY NODEFDAC export DB_LOCALE CLIENT_LOCALE
Управление заданиями - это механизм для отслеживания процессов, которые порождаются из текущего сеанса работы с терминалом. Можно запустить любое число заданий. Они могут работать, завершиться или находиться в других состояниях. Для управления заданиями можно использовать несколько команд, чтобы проследить результаты их работы или потребовать от системы уведомления об окончании задания.
Фоновый режим позволяет продолжить использование сеанса работы с терминалом, пока выполняется команда. Для запуска команды в фоновом режиме, достаточно к команде добавить символ амперсанд (&). Командный интерпретатор вернет номер задания и идентификатор процесса:
$ make & [2] 254 $
Если задание не требует от пользователя ввода, оно продолжает свою работу до полного завершения. Если команде нужен ввод, она переходит в состояние ожидания, на экран выводится соответствующее уведомление, которое выглядит примерно так:
[1] + Suspended (tty input) programm 0
В данном случае в ожидании ввода приостановилось выполнение программы programm. Пользователю необходимо перевести из фонового режима в привилегированный и выполнить ввод.
С помощью команды jobs пользователь имеет возможность просмотреть состояние своих заданий и получит список всех заданий запущенных в сеансе работы с терминалом.
$ jobs [1] Stopped (user) du [2]- Stopped (user) du -a /home/intdbi [3]+ Stopped (user) du -r /home/intdbi $
Команда jobs принимает два флага. Флаг -l включает идентификатор процесса с номером задания.
$ jobs -l [1] 1351 Stopped (user) du [2]- 1381 Stopped (user) du -a /home/intdbi [3]+ 1383 Stopped (user) du -r /home/intdbi $
Флаг -р заменяет номер задания на идентификатор процесса.
$ jobs -p 1351 1381 1383 $
Идентификатор процесса может использоваться при обращении к команде ps.
Номер задания позволяет командному интерпретатору наблюдать за процессами. Его можно рассматривать как головной элемент группы процессов, поскольку пользовательское задание порождает любые команды либо в конвейере, либо, как подзадания.
Команда fg переводит задания в привилегированный режим. При наличии приостановленного задания, его можно сделать привилегированным (перевести на передний план) с помощью команды fg #номер_задания (или fg номер_задания в bash). После этого задание либо выведет на экран сообщение о том, что ему нужно от терминала, либо будет принимать ожидаемый ввод. Переведя задание в привилегированный режим, можно приостановить его выполнение, нажав комбинацию калвиш Ctrl-Z, и заняться им позже.
Любое задание из списка, предоставленного командой jobs, доступно, если пользователь захочет сделать его привилегированным, даже в том случае, когда оно уже работает в фоновом режиме. Если в этом списке приведено только одно задание, то при использовании команды fg пользователю не нужно задавать его номер. Если номер задания не задан, предполагается текущее задание.
С помощью команды bg можно возобновить в фоновом режиме работу приостановленного или остановленного задания. Для этого нужно указать соответствующий номер задания, после чего оно перейдет в фоновый режим, и будет работать до своего завершения, или пока ему снова не потребуется ввод с терминала.
Это последняя существенная команда управления заданиями. При вводе wait приостанавливается работа командного интерпретатора до тех пор, пока не будут завершены все фоновые задания. Сюда входят и любые остановленные задания, поэтому при вводе wait стоит убедиться, что все фоновые задания работают. Команда wait может также принимать в качестве параметра номер задания. В этом случае командный интерпретатор приостанавливается до тех пор, пока не завершится выполнение указанного задания.