В интерпретаторе Shell имеется возможность определения функций. Функции похожи на процедуры Shell за исключением того обстоятельства, что они находятся постоянно в памяти (резидентны) и поэтому выполняются самим интерпретатором. Основная форма определения функции:
name ( ) { список; }
Список может включать в себя любую команду из ранее описанных. Функции могут быть определены в одной части процедуры Shell и могут затем вызываться любое необходимое число раз. Ниже приводится пример функции с именем "getyn":
# Ответ "да" или отсутствие ответа - возвращение ненулево-
# го значения в случае "нет"
getyn ( ) {
while echo "$* (y/n)? C" >& 2
do read yn rest
case $yn in
[yY] return 0 ;;
[nN] return 1 ;;
*) echo "Please answer y or n" >&2 ;;
esac
done
}
В этом примере функция добавляет "(y/n)?" к выводу и ожидает ввода символов "Y", 'y", "N" или "n", возвращая 0 или 1. Если введены какие-либо другие символы, функция просит пользователя исправить ввод.
Функции можно использовать так же как и любые другие команды. Вызов функции getyn может быть, например, такой:
getyn "Do you wish to continue" || exit
Надо помнить, что при этом позиционные параметры $1,$2,... устанавливаются равными значениям аргументов функции. Т.к. команда exit, использованная в функции, завершает работу всей процедуры, то для возвращения обратно в процедуру требуется применять команду return.
Переопределение направления ввода/вывода и управляющие команды
Обычно Shell не разветвляет свои процессы и не создает новые подоболочки при обнаружении управляющих команд (отличных от обычных скобок). Однако, каждая команда в конвейере выполняется как отдельный процесс для того, чтобы правильно направить ввод и вывод для каждой команды. Когда переадресация ввода-вывода явно указана в команде, тогда запускается отдельный процесс для этой команды. Таким образом, когда команды if, while, until, case и for применяются в конвейере, состоящем более чем из одной команды, Shell разветвляется и подоболочка выполняет управляющую команду. При этом надо помнить:
1. Все изменения, сделанные с переменными во время выполнения управляющей команды, не сохраняются при ее завершении. Это аналогично группированию команд с помощью обычных скобок.
2. Управляющие команды при переадресации выполняются немного медленнее.
Обмен между файлами: команда (.)
Командная строка в виде :
. proc
указывает Shell считать команды из proc, не создавая нового процесса. Изменения, сделанные с переменными в proc, сохраняются после завершения команды (.). Это удобный способ инициализации сразу нескольких переменных Shell. Обычно эту команду используют для реинициализации интерпретатора Shell верхнего уровня чтением файла .profile:
. .profile
Управление прерываниями: trap
Процедуры Shell могут использовать команду trap для отключе ния сигнала (когда это требуется) или для переопределения его действия. Синтаксис команды trap следующий:
trap аргумент список_сигналов
Здесь аргумент - это текст, который интерпретируется как список команд, а список_сигналов состоит из одного или более номеров сигналов, описанных в UNIX Programmer's Reference. Наиболее важными сигналами являются:
номер
сигнал
Выход из Shell
HANGUP
символ INTERRUPT (DELETE или RUB OUT)
QUIT (Ctrl-\)
KILL (не может быть перехвачен или проигнорирован)
Изменение сегментации (не может быть пере хваченили проигнорирован)
Сигнал завершения работы программы
Команды, указанные в аргументе, просматриваются по меньшей мере один раз, когда Shell встречает команду trap. Из-за этого мы советуем вам заключать эти команды в одинарные, а не двойные кавычки, т.к. первые вызывают немедленную подстановку команд и переменных. Это становится важным, когда вы, например, хотите удалить временные файлы и имена этих файлов еще не определены при первом чтении команды trap интерпретатором. Следующая процедура напечатает в качестве информации для пользователя имя текущего каталога, в котором выполнялось задание, когда было получено прерывание:
trap 'echo Directory was `pwd` when interrupted` 2 3 15
for i in /bin /usr/bin /usr/gas/bin
do
cd $i
# здесь идут команды, исполняемые в каталоге $i
done
Та же самая процедура, но с двойными кавычками выполняет другие действия. Следующая команда:
trap "echo Directory was `pwd` when interrupted" 2 3 15
печатает имя каталога, из которого впервые стала выполняться процедура.
Сигнал с номером 11 никогда не может быть перехвачен, т.к. он необходим самому интерпретатору для правильного распределения памяти. Ноль интерпретируется командой trap как сигнал, образующийся при выходе из Shell. Это происходит любо при выполнении команды exit, либо при достижении конца процедуры. Если аргумент не указан, тогда при получении любого из сигналов выполняются принятые в системе по умолчанию действия. Если аргумент является пустым ("" или ''), тогда сигналы из списка сигналов игнорируются Shell'ом.
Команда trap наиболее часто используется для проверки того, что все временные файлы удалены после завершения процедуры. Предыдущий пример можно было записать так:
temp=$HOME/temp/$$
trap 'rm -f $temp; exit' 0 1 2 3 15
ls > $temp
# здесь идут команды, использующие $temp
В этом примере, как только любой из сигналов 1, 2, 3 или 15 будет получен процедурой или сама процедура будет завершаться, то будут выполнены все команды, записанные в одинарных кавычках. Команда exit здесь необходима, т.к. в противном случае Shell будет читать команды дальше после того места, где был получен сигнал. В некоторых случаях Shell продолжает чтение команд после выполнения команды trap. Приводимая ниже процедура берет каждый подкаталог из текущего каталога, переходит туда, печатает его имя и выполняет команды, вводимые с терминала до тех пор, пока не будет введен символ конца файла (Ctrl-d) или не будет получено прерывание. Конец файла вызывает возврат из команды read с ненулевым кодом завершения, тем самым прекращая цикл while и запуская цикл для следующего имени каталога. Прерывания игнорируются во время выполнения вводимых команд, но вызывают завершение, если процедура в этот момент ждет ввода:
d=`pwd`
for i in *
do if test -d $d/$i
then cd $d/$i
while echo "$i:"
trap exit 2
read x
do trap : 2
# игнорирование прерываний
eval $x
done
fi
done
Одновременно может быть указано несколько команд trap. Если различные сигналы получены одновременно, они обрабатываются по очереди в возрастающем порядке. Чтобы определить, перехват каких прерываний установлен в данный момент, введите:
trap
Важно понимать определенные моменты в том, как Shell обрабатывает команду trap. Когда Shell получает сигнал (отличный от 11), он передается во все дочерние процессы, выполняющиеся в данный момент. Когда эти (синхронные) процессы завершатся, нормально или ненормально, Shell просматривает все установленные перехваты и выполняет соответствующую команду trap. Этот процесс идет сверху вниз, за исключением перехватов, установленных во время заг рузки. В этом случае возможно, что нет никаких дочерних процессов и Shell перед тем, как просматривать установленные прерывания, ждет завершения первого процесса, запущенного после получения сигнала.
Когда сигнал переопределяется в процедуре Shell, это не переопределяет сигналов для программ, вызванных из этой процедуры. Для внутренних команд Shell обычно обрабатывает прерывания после завершения команды. Исключение из этого правила сделано для команды read, чьи прерывания обрабатываются немедленно, так что команда read может быть прервана во время ожидания ввода.