Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

VPS в России, Европе и США

Бесплатная поддержка и администрирование

Оплата российскими и международными картами

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

2004 г

Основы операционной системы UNIX

Copyleft (no c) - 1996-2004 В. Кравчук, OpenXS Initiative, идея, составление, перевод, примеры
Назад Оглавление Вперёд

Командный интерпретатор

Пользователь ОС UNIX общается с системой через командный интерпретатор (shell). Через него происходит доступ к командам, файловой системе и другим функциям ядра UNIX. Это обычная программа (т.е. не входит в ядро операционной системы UNIX). Ее можно заменить другой или использовать несколько разных версий. Наиболее известны следующие версии:

sh
Классический интерпретатор версии UNIX V7, иначе называемый по фамилии автора Bourne shell.

ksh
Интерпретатор Korn shell, дополняющий классический shell возможностями работы с заданиями пользователя, историей работы и позволяющий редактировать командную строку при помощи команд, аналогичных vi. Является фактически стандартом для POSIX-совместимых систем, в частности, UNIX System V.

csh
Стандартный интерпретатор BSD UNIX и производных от него систем. Отличается улучшенными диалоговыми возможностями, способом присваивания и экспортирования переменных в среду, управляющими конструкциями и рядом других моментов; тоже поддерживает историю и редактирование командной строки. Главное и, по моему мнению, отрицательное его отличие от других интерпретаторов, - это свои управляющие конструкции, не совпадающие с sh.

bash
Свободно распространяемый в виде исходных текстов интерпретатор, называемый "Bourne another shell", объединяющий все лучшее из остальных интерпретаторов с удобными возможностями редактирования командной строки и работы с историей команд. В настоящее время - фактический стандарт.

В рамках этого курса мы будем рассматривать, в основном, средства, не выходящие за пределы возможностей командных интерпретаторов 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 Выполнить 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 Список символов, разделяющих слова в аргументах
$MAIL Файл почтового ящика. Командный интерпретатор информирует пользователя о получении почты в указанный файл.
$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 имеет следующий синтаксис:

<цикл for> ::=
for <имя переменной> [in <список слов>] do <команды> done
<список слов> ::=
<слово>{<пробел> <слово>}
<команды> ::=
<команда> {<; или перевод строки> <команда>}

Переменная последовательно получает значение очередного слова из списка, и для этого значения выполняются команды в теле цикла. Цикл завершается, когда пройден весь список слов. По умолчанию в качестве списка слов используются аргументы командной строки.

Рассмотрим пару примеров таких циклов:

$ 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> ::=
while <команды> do <команды> done
<оператор until> ::=
until <команды> do <команды> done

Выполняются команды, задающие условие, и проверяется код возврата последней из них. Если это ноль (истина), выполняются команды в теле цикла 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 <слово> in
<описание варианта> ) <команды> ;;
{<описание варианта> ) <команды> ;; }
esac
<описание варианта> ::=
<шаблон> { | <шаблон>}
<команды> ::=
<команда> {<разделитель> <команда>}
<разделитель> ::=
<перевод строки> | ;

Слово (обычно - значение переменной) сравнивается последовательно с шаблонами. Если произошло сопоставление (по правилам сопоставления шаблонов имен файлов) выполняются команды, соответствующие данному варианту и оператор завершается. Учтите, что шаблон *) сопоставляется с любым словом, и, тем самым, задает вариант по умолчанию.

В шаблонах оператора case символы . и /, в отличие от шаблонов имен файлов, не обязательно задавать явно.

Условный оператор

Командный интерпретатор поддерживает условный оператор следующего общего вида:

<условный оператор> ::=
if <команды> then <команды>
{elif <команды> then <команды>}
[else <команды>]
fi

Выполняются команды после if и проверяется код возврата последней из них. Если это 0 (истина) выполняются соответствующие команды после then и выполнение оператора завершается. Если же это не 0 (ложь), то при наличии конструкций elif выполняются последовательно соответствующие команды-условия и, если они возвращают код 0, команды после then, а затем оператор завершается. Если ни одно из условий не было истинным, выполняются команды в части else и оператор завершается.

В качестве условия в условном операторе может использоваться любая команда. Однако, имеется стандартная команда для проверки условий в традиционном понимании. Это команда test, представленная в следующем разделе.

Проверка условий в командном интерпретаторе

Команда test имеет следующий синтаксис:

<команда 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, устанавливающая с момента выполнения обработчик в виде последовательности команд (одним словом) для всех перечисленных сигналов. Эта команда имеет следующий синтаксис:

<оператор trap> ::=
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> ::=
let <аргумент> {<аргумент>}

Вот как ее можно использовать:

$ let a=5
$ echo $a
5
$ let a=a*a+34/2
$ echo $a
42
$ let "a = 7"
$ echo $a
7

Обратите внимание, что если вокруг знака равенства идут пробелы, необходимо брать выражение в кавычки. Команда let требует, чтобы выражение было одним словом. Кроме того, для обращения к значению переменной в этой команде не нужно использовать метасимвол $.

Команда expr

Одной из стандартных программ-калькуляторов является программа 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. Функции читаются и хранятся внутренне командным интерпретатором. Функции выполняются как команды, причем аргументы передаются как позиционные параметры. Синтаксис определения функции следующий:

<определение функции> ::=
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 может также принимать в качестве параметра номер задания. В этом случае командный интерпретатор приостанавливается до тех пор, пока не завершится выполнение указанного задания.

Назад Оглавление Вперёд
VPS/VDS серверы. 30 локаций на выбор

Серверы VPS/VDS с большим диском

Хорошие условия для реселлеров

4VPS.SU - VPS в 17-ти странах

2Gbit/s безлимит

Современное железо!

Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

🔥 Anti-DDoS защита 12 Тбит/с!

Новости мира IT:

Архив новостей

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...