19.7. Интерпретатор команд bash
Интерпретатор команд — это программа, выполняющая команды пользователя. Стандартным интерпретатором (или оболочкой) является bash (Bourne Again Shell). Достаточно распространенными также являются следующие интерпретаторы: sh, ash, bsh, tcsh, csh, zsh. Список установленных в вашей системе оболочек находится в файле /etc/shells. Команды оболочки можно вводить в командной строке, а можно оформить в виде сценария. Сценарий — это файл, содержащий команды оболочки. Создайте обыкновенный текстовый файл и сделайте его исполнимым. Система выполнит указанную последовательность команд. Для того, чтобы система узнала, какую оболочку нужно использовать, первая строка сценарий должна содержать полное имя сценария.
Например:
#!/bin/bash
Между символами # и ! не должно быть пробелов. Для обработки сценария вы можете использовать любую программу (естественно, она должна понимать синтаксис файла), а не только указанную в файле /etc/shells. Например, вы можете написать:
#!/usr/bin/my_proga
Оболочка при этом запустит программу /usr/bin/my_proga и передает имя файла сценария в качестве параметра. Если вы напишете:
#!/usr/bin/my_proga -f
то оболочка выполнит команду:
#!/usr/bin/my_proga -f <имя_сценария>
Создадим небольшой сценарий, который будет очищать экран и выводить на него ваше имя. Имя можно передать как параметр. Назовем наш сценарий test:
#!/bin/bash
# Это комментарий
clear
echo $1
Теперь рассмотрим все по порядку. С первой строкой, я думаю, все ясно. Вторая строка очищает экран. Третья строка выводит информацию, которая передана сценарию как первый (1) параметр. Запустите сценарий следующим образом:
./test Vasya Pupkin
На экране будет напечатан первый параметр, то есть слово Vasya. Вы можете немного изменить сценарий, чтобы он выводил оба параметра:
echo $1 $2
Если вы хотите передать фамилию, имя и отчество, то нужно использовать следующую команду:
echo "$1"
При этом не нужно явно указывать три параметра, просто интерпретатор не будет использовать пропуск для разделения параметров и все, что вы передадите ему, будет считаться одним параметром. При этом, если вы запустите сценарий с параметром Vasya Pupkin, на экране увидите Vasya Pupkin. А если укажете параметр Ivanov Ivan Ivanovich, сценарий так и напечатает Ivanov Ivan Ivanovich.
19.7.1. Каналы и списки
Материал этого и следующего пункта дополняет гл. 5, в которой рассматривалось перенаправление ввода/вывода. Поэтому я не буду подробно описывать сам механизм этих процессов, а ограничусь лишь несколькими примерами, чтобы напомнить вам гл. 5.
С помощью каналов вы можете перенаправить стандартный вывод одной программы на стандартный ввод другой. Например:
cat /var/log/secure | less
ps –ax | grep "$UID" | less
В первом случае стандартный вывод команды cat (содержимое файла /var/log/secure) перенаправляется на стандартный ввод программы less, которая обеспечивает поэкранный вывод информации. Вторая строка выводит список всех процессов, принадлежащих данному пользователю. Первая команда ps-ax выводит список всех запущенных в системе процессов, вторая (grep "$UID") ищет фрагмент текста, содержащий идентификатор пользователя (UID) и выводит результат на стандартный вывод, то есть на стандартный ввод команды less. $UID является переменной окружения, которая содержит идентификатор пользователя. О переменных окружения поговорим немного позже.
Интерпретатор позволяет указывать списки команд в командной строке. Например:
lpr file.txt; lpq
При этом сначала выполняется команда постановки задания в очередь печати, а потом проверяется состояние принтера. Теперь более сложный пример:
ps –ax | head –n 1; ps –ax | grep httpd
Сначала выполняется команда ps для печати заголовка таблицы, а потом — для вывода информации о демоне httpd.
Можно использовать операции конъюнкции и дизъюнкции, например:
command1 && comand2
command3 || command4
Команда command1 будет выполнена в случае успешного завершения команды command1 (возвратный код равен 0). Команда command4 будет выполнена, если код возврата команды command3 не равен 0. Самый простой пример — создание и изменение каталога:
mkdir mydir && cd mydir
Обычно интерпретатор bash выполняет команды в синхронном режиме, то есть после запуска программы ожидает ее завершения. Однако можно запустить программу и в асинхронном режиме, то есть без ожидания ее завершения. Для этого нужно использовать символ «&» в конце команды, например:
program &
19.7.2. Перенаправление ввода/вывода
Перенаправление ввода/вывода уже рассматривалось в гл. 5, поэтому я лишь напомню общий формат команд:
команда > (>>) файл
список > (>>) файл
Как вы уже знаете, при использовании одного знака больше файл, в который переназначен вывод, будет перезаписан, а при использовании двойного больше информация будет добавлена в конец файла. При использовании списка команд список нужно взять в фигурные скобки:
{date; free; who; } > logfile
Перенаправление ввода/вывода может быть использовано и в обратном направлении. Например, для печати списка URL достаточно выполнить команды:
lpr << URL
http://www.linux.ru
http://linux.ru.net
http://www.linux.org
URL
19.7.3. Подоболочки
При написании сценариев вы можете использовать механизм подоболо-чек. Если вы знакомы с каким-нибудь языком программирования, то должны знать об области распространения действия переменных. Существуют глобальные и локальные переменные. Первые действуют во всей программе, а вторые только внутри определенного блока, например, функции. Если в локальной функции определена переменная с таким же именем, что и одна из глобальных переменных, в этом локальном блоке будет использоваться значение локальной переменной. Далее приведен простейший пример программы, использующей локальные и глобальные переменные на языке Pascal:
Program Test;
Var A : integer; {глобальная переменная}
procedure GetLocal ;
var A: integer; {локальная переменная)
begin
А:= 10;
writeln(A);
end;
begin
А:=7;
writeln(A);
GetLocal(A);
writeln(A);
end;
При запуске программа выведет на экран:
7
10
7
В bash наблюдается нечто подобное. Этот блок называется подоболочкой. Если список команд заключен в фигурные скобки, то он выполняется в текущей оболочке, а если в обыкновенные, то в подоболочке. Итак, рассмотрим выполнение почти аналогичных сценариев:
#!/bin/bash
# Сценарий 1
NUM="one"; (NUM="two"; echo $NUM;);
echo $NUM
#!/bin/bash
# Сценарий 2
NUM="one"; {NUM="two"; echo $NUM;};
echo $NUM
Сценарий 1 выведет на экран следующую информацию:
two
one
а сценарий 2:
two
two
С помощью механизма подоболочек вы можете создавать более гибкие сценарии. С его помощью, например, можно временно изменять рабочий каталог:
pwd; (cd /tmp; pwd;); pwd
Сценарий выведет на экран:
/home/user
/tmp
/home/user
19.7.4. Переменные и массивы
Пример простейшей переменной мы уже рассмотрели выше. Переменные в bash не нуждаются в предварительном описании, как в других языках, например, в том же Pascal. Все переменные в bash — текстовые. Имя переменной должно начинаться с буквы и может состоять из латинских букв, цифр, знака подчеркивания. Нельзя определять функцию и переменную с одинаковыми именами. Чтобы воспользоваться значением переменной, нужно использовать символ доллара перед именем переменной:
VAR="my var"
echo $VAR
Как я уже говорил, все переменные текстовые. Например, когда вы присваиваете переменной значение VAR=13, то это будет не числовое значение, а строка из двух символов. Если присваиваемое значение имеет пробелы, его нужно взять в кавычки:
VAR="value 1"
Присвоить значение переменной можно с помощью встроенной команды read:
echo –n "Enter value: "
read x
echo $x
Параметр –n команды echo не выводит символ новой строки в конце сообщения «Enter value:», то есть не переводит строку. Команда read читает значение, введенное пользователем с клавиатуры, и записывает его в переменную х. Последняя команда выводит только что введенное значение. При использовании команды read можно указывать несколько переменных:
read a b с
Пользователь должен ввести значения переменных, разделяя их пробелами. Для окончания ввода нужно нажать «Enter». Если введено меньше значений, чем нужно, оставшимся переменным будет присвоена пустая строка. А если больше, то лишние значения будут проигнорированы.
Интерпретатор bash использует следующие метасимволы, имеющие для него особое значение:
* ? ; & ( ) | <><возврат_каретки><табуляция><пробел>
Для того, чтобы использовать эти символы как они есть, нужно их цитировать с помощью символа \. Например, символ перевода строки можно цитировать как \n, символ табуляции как \t, а символ вопроса как \?
Особое значение при присваивании переменным значений имеют кавычки. Все символы, заключенные в одинарные кавычки ' ' представляют самих себя. Между двойными кавычками " " выполняются команды и подстановки значений. Символы "\", ",", " ' ", "$" могут цитироваться с помощью обратной наклонной черты: \\, \$, \'
Для обозначения параметров командной строки используются специальные переменные, указанные в табл. 19.36.
Специальные переменные Таблица 19.36
Название переменной Значение $0 Имя выполняемой команды. Для сценария — путь, указанный при его вызове. Если вы знакомы с языком программирования Pascal, эта переменная должна вам напоминать вызов функции ParamStr(0) $n (где n – номер параметра, $1, $2, …) Обращение к параметру с номеров n. Для Pascal — это вызов функции ParamStr(n) $# Число параметров, которые были указаны при вызове сценария. Аналогична вызову функции ParamCount в Pascal $* Все параметры, заключенные в кавычки: "$1 $2 …" $@ Все параметры, каждый из которых заключен в кавычки "$1" "$2" … $? Код завершения последней команды $$ Номер текущего процесса (PID) $! Номер(PID) последнего асинхронного процесса (команды, выполненной в фоновом режиме)
Пример.
echo "Все параметры: "
echo $*
Интерпретатор bash поддерживает одномерные массивы с неограниченным числом элементов. В других оболочках существуют определенные ограничения на массивы, например, в ksh максимальное число элементов массива ограничено 1024-мя элементами.
Присвоить значение элементу массива можно с помощью такой конструкции:
Имя_массива[индекс]=значение
Например:
А[1]=23
А[3]=54
А[0]=77
Нумерация элементов начинается с ноля. Тип элементов массива, как и тип переменных, текстовый. Присвоить значение элементам массива можно также с помощью инструкции set. Например, выражение:
set –A array 3 56 77 12
аналогично выражениям:
array[0]=3
array[1]=56
array[2]=77
array[3]=12
Вышеприведенные способы инициализации массивов могут применяться как в bash, так и в ksh, а также и в других оболочках. Но, тем не менее, существует еще один способ, который работает только в bash:
array = ([0]=3 [3]=12 [2]=77 [1]=5б)
Обратиться к значению элемента можно следующим образом:
${имя_массива[индекс]}
Например, вывести значение первого (нулевого) элемента массива можно так:
echo {$array[0]}
Обратиться ко всем элементам массива сразу можно с помощью одного из выражений:
${имя_массива[*]}
или ${имя_массива[@]}
Если значение хотя бы одного элемента массива может содержать пробелы, используйте второе выражение:
echo ${array[@]}
Особое значение имеют переменные окружения. У каждого процесса есть своя среда — множество доступных ему переменных. Обыкновенные переменные доступны только из локального процесса. Чтобы использовать их значения в порождаемых процессах, нужно использовать встроенную команду export. После того, как вы экспортируете переменные, они будут доступны всем порожденным процессам.
Выражение export:
export имя
export имя=значение
export имя имя имя …
export имя=значение имя имя=значение…
Как видите, можно экспортировать уже инициализированную переменную (которой уже присвоено значение), а можно выполнить инициализацию переменной непосредственно при экспорте. Экспортировать можно как одну переменную, так и целый список.
Для деактивизации переменной используется команда unset: unset имя
Каждому процессу доступны переменные оболочки, приведенные в табл. 19.37.
Переменные оболочки Таблица 19.37
Название переменной Значение PWD Текущий каталог UID Идентификатор пользователя, запустившего сценарий REPLY Последняя строка, введенная с помощью команды read RANDOM Случайное число в диапазоне от 0 до 32767 SECONDS Число секунд, прошедшее с момента запуска оболочки IPS (Internal Filed Separator) Внутренний разделитель полей. Используется синтаксическим анализатором и командой read для разделения строчки на слова. По умолчанию его значение равно " \t\n", где: « » — пробел; «\t» — символ табуляции; ,«\n» — символ новой строки HOME Домашний каталог PATH Путь вызова LOGNAME Имя пользователя, которое использовалось для входа в систему MAIL Имя файла, в который поступает электронная почта SHELL Имя интерпретатора команд TERM Тип терминала пользователя
Пример.
echo "$НОМЕ"
19.7.5. Подстановка команд и арифметических выражений
В гл. 13 (п. 13.5) мы уже сталкивались с подстановкой команд. Тогда переменной DT присваивался результат выполнения команды date:
DT=`date`
Как я уже писал, при подстановке команд нужно использовать обратные одинарные кавычки (они расположены под символом тильды на клавиатуре). Подставлять можно не только одну команду, а целые списки команд:
USERS=`who | wd –l`
UP=`date; uptime`
В первом случае мы получим количество пользователей работающих в системе, а во втором — последовательно записанные результаты выполнения команд date и uptime.
Подставлять результаты выполнения можно не только в переменные, а и в другие команды, например:
grep `id –un` /etc/passwd
Данная команда ищет в файле /etc/passwd вхождение результата выполнения команды id –un
Подстановка арифметических выражений осуществляется с помощью конструкции $(( выражение )) Например:
А = $(( (10+51/2 ))
echo $A
При этом на экране вы увидите число 7, а не 7.5, потому что используется целочисленное вычисление. Пример. Количество часов, прошедшее с момента запуска оболочки:
hrs = $(( $SECONDS/3600 ))
19.7.6. Управляющие структуры и циклы
К управляющим структурам относятся:
• Конструкция if-fi.
• Конструкция case-esac.
Конструкция if-fiОбщий синтаксис конструкции if-fi:
if список1 then
список2
elif
список3
then
список4
else
список3
fi
Конструкция if-fi работает так же, как и в других языках программирования. Если список1 (условие) истинный, выполняется список1, иначе выполняется список3 и проверяется его истинность и т.д. Допускается неограниченная вложенность операторов if. Например:
if uuencode myfile myfile > myfile.uu; then
echo "Успешное конвертирование";
else
echo "Ошибка";
fi
Можно использовать сокращенный вариант:
if список1 then
список2
fi
Например:
if [ $? –ne 0 ]; then echo "Ошибка. См. файл протокола"; fi;
Вместо списка команд удобно использовать команду test или выражение [условие]. Например, следующие выражения аналогичны:
test –e /etc/passwd
[-е /etc/passwd]
И первое, и второе выражение проверяют существование файла /etc/passwd. Другие опции команды test представлены в табл. 19.38.
Опции команды test Таблица 19.38
Опция Возвращаемое значение и описание -d файл Истина, если файл существует и является каталогом -е файл Истина, если файл существует -f файл Истина, если файл существует и является простым файлом -k файл Истина, если файл существует и для него установлен бит односторонней операции -L файл Истина, если файл существует и является ссылкой -r файл Истина, если файл существует и доступен для чтения -s файл Истина, если файл существует и его размер больше 0 -х файл Истина, если файл существует и является исполнимым -w файл Истина, если файл существует и доступен для записи -о файл Истина, если файл существует и принадлежит данному пользователю -z строка Истина, если длина строки равна 0 -n строка Истина, если длина строки не равна 0
Команда test, в случае успешного завершения, возвращает значение истина, то есть 0 — успешное завершение. Если в скобках стоит непустое слово, test возвратит тоже 0, например:
[ word ]; echo $?
0
[]; echo $?
1
В первом случае возвращается истина (true), на экран выводится ноль — код удачного (безошибочного) завершения программы. Во втором случае на экран выводится единица — команда test возвратила значение ложь (false). Сравнение строк осуществляется следующим образом: выражения str1 = str2 или str1 == str2 истинны, когда строки str1 и str2 равны. Обратите внимание: между двумя символами равно не должно быть пропуска!
Символ ! инвертирует любое условие команды test, например, выражение str1 != str2 будет истинным, когда строки str1 и str2 не равны между собой. Символ ! является символом логической операции NOT (отрицание). Кроме этого символа, можно использовать опции команды –о и –а, которые обозначают логические операции ИЛИ (OR) и И (AND). Например:
str="word"; export str; ["$str" –a –f /etc/passwd]; echo $?
0
str=""; export str; ["$str" –a –f /etc/passwd]; echo $?
1
В первом случае непустая строка str возвращает истину, опция —f возвращает также истину, потому что файл /etc/passwd существует всегда. Результат операции И: истина И истина = истина, поэтому на экране вы увидите 0.
Во втором случае пустая строка str возвратит ложь, а опция –f возвращает истину. Результат операции И: ложь И истина = ложь. Если вы забыли законы логики, освежите свои знания с помощью табл. 19.39.
Логические операции Таблица 19.39
AND True False OR True False XOR True False True True False True True True True False True False False False False True False False True False
Операция XOR — это исключающее ИЛИ. Данная операция не используется при создании сценариев с помощью интерпретатора bash.
Для сравнения целых чисел используются опции команды test,приведенные в табл. 19.40.
Сравнение целых чисел Таблица 19.40
Опция Описание -eq Равно -ne Не равно -It Меньше -gt Больше -le Меньше или равно -ge Больше или равно
Интерпретатор bash воспринимает строки, как целые числа. Если нужно обнулить строку, то это достигается таким присваиванием: х=0.
Пример.
х=124 ; export х ; [ 111 –It "$х" ] ; echo $?
0
Поскольку 111 меньше, чем 124, на экране вы увидите 0 (истина).
Примечание. Во всех примерах, вы, наверное, заметили использование команды export. Это необходимо для того, чтобы порожденному процессу (не забывайте: test — это отдельная программа) переменнаях была доступна.
Теперь, когда мы уже знакомы с конструкциями test и if, рассмотрим небольшой пример, демонстрирующий вложенность операторов if и использование команды test. Пример приведен в листинге 19.2.
Листинг 19.2. Пример вложенности операторовecho –n "Какую оценку ты получил сегодня по программированию? "
read x
if [ $х = 5 ]
then echo "Отлично !"
elif [ $х = 4 ]
then echo "Хорошо"
elif [ $x = 3 ]
then echo "Удовлетворительно"
elif [ $x = 2 ]
then echo "Надо бы пересдать"
else
echo "Как вообще можно было получить такую оценку???"
fi
Если вы введете 5, сценарий отобразит на экране слово «Отлично», при вводе 4 вы увидите слово «Хорошо» и так далее. Если вы введете 0, 1 или число больше пяти, вы увидите на экране последнюю фразу: «Как вообще можно было получить такую оценку???».
Конструкция case-esacКонструкция выбора (case — выбор) имеет следующий синтаксис:
case значение in
шаблон1) список1 ;;
…
шаблонN) списокN ;;
esac
Рассмотрим сценарий (см. листинг 19.3), аналогичный сценарию 19.2, но использующий конструкцию case вместо if.
Листинг 19.3. Пример использования оператора caseecho –n " Какую оценку ты получил сегодня по программированию? "
read x
case $x in
5) echo "Отлично !" ;;
4) echo "Хорошо" ;;
3) echo "Удовлетворительно" ;;
2) echo "Надо бы пересдать" ;;
*) echo "Как вообще можно было получить такую оценку???" ;;
esac
Работа сценария аналогична первому сценарию: при вводе оценок 2, 3, 4, 5 будут отображены соответствующие сообщения, а во всех остальных случаях — последнее сообщение.
Примечание. Структура оператора case больше напоминает структуру оператора case в языке Pascal, чем в языке С. Последняя строка выбора с шаблоном *) будет выбрана, когда не произойдет ни одного совпадения с ранее указанными шаблонами. Если же произошло совпадение с шаблоном шаблонN, то будет выполнен список списокN. После выполнения списка команд списокN будет произведен выход из структуры case —так же как и в Pascal. В языке С наблюдается нечто другое: если будет обнаружено совпадение, скажем с шаблоном3, то будут выполнены последовательности операторов 3, 4, 5, … N. Чтобы прервать выполнение блока case в языке С нужно использовать оператор break. В bash же такого нет.
Если для одного списка команд нужно описать два или более шаблонов, используется символ | (OR).
case num in
1|2|3) echo "1 or 2 or 3" ;;
4|5) echo "4 or 5" ;;
*) echo "other num" ;;
esac
ЦиклыИнтерпретаторы bash и ksh поддерживают циклы for, while, until, select, a интерпретатор sh только for и while.
Синтаксис цикла for:
for имя_переменной in список1 do
список2
done
Простой пример:
for i in 1 2 3 4 5; do echo $i; done
На экране вы увидите:
1 2 3 4 5
Еще раз напомню, что любой список в bash должен заканчиваться точкой с запятой. Начинающие «программисты» делают много ошибок, связанных именно с этой особенностью списков. Пример использования: построчно вывести содержимое файла /etc/passwd вы можете с помощью такого цикла:
for str in `cat /etc/passwd` do
echo "$str";
done
Цикл for закончит свою работу, когда будет обработан последний элемент списка, в данном случае, когда на экран будет выведена последняя строка файла /etc/passwd.
Синтаксис цикла while:
while список1 do
список2
done
Цикл while будет выполняться, пока условие, заданное в списке список!,
будет истинным. Поэтому цикл while иногда называют циклом с истинным условием. Например,
х=1
while [$х –It 10]
do
echo $х
X = $(( $Х + 1 ))
done
На экране вы увидите:
1 2 3 4 5 6 7 8 9
Когда переменная х примет значение 10, цикл завершит свою работу, так как программа test вернет значение false (x уже не меньше, а равен 10). Цикл until (до) имеет похожую структуру, но выполняется несколько иначе:
until список1 do
список2
done
Цикл until прекратит работу, когда условие, указанное в списке список1, станет истинным. Другими словами, он будет выполняться пока это условие ложно. Цикл while, наоборот, выполняется пока условие истинно. Лучше всего разница между этими циклами видна на примере (сравните листинги 19.4 и 19.5)
Листинг 19.4. Цикл whileх=1;
while ! [$х –ge 10]
do echo $x echo $x
X = $ (($Х + 1))
done
Листинг 19.5. Цикл untilх=1;
until [$x –ge 10]
do
X=$(($Х + 1))
done
Циклы, приведенные в листингах 19.4 и 19.5, выведут одинаковую последовательность цифр на экран:
1 2 3 4 5 6 7 8 9 10
Рассмотрим еще один полезный цикл select, который позволяет создавать нумерованные пункты меню. Его конструкция такова:
select имя in список1
do
список2
done
Пример:
echo "Выберите файл для просмотра
select file in /home/den/temp/* QUIT
do
if [-e $file]; then less $file
else
break
done;
В моем временном каталоге /home/den/temp находится всего два файла — file.txt, proto.txt, поэтому на экране монитора будет отображено следующее:
Выберите файл для просмотра:
/home/den/temp/.
/home/den/temp/..
/home/den/temp/file.txt
/home/den/temp/proto.txt
QUIT
Первые два файла — это ссылки на текущий и родительский каталоги. Пункты меню 3 и 4 — это файлы, которые можно выбрать для просмотра. QUIT — это последний элемент списка. При его выборе сработает оператор break и цикл завершится.
19.7.7. Подстановка переменных
Мы уже рассмотрели подстановку команд, сейчас рассмотрим подстановку переменных (см. табл. 19.41).
Подстановка переменных Таблица 19.41
Конструкция Описание ${переменная:-значение} Если переменная определена и не является пустой строкой, подставляется ее значение, иначе подставляется значение, указанное в конструкции. Реальное значение переменной при этом не изменяется ${переменна:=значение} Значение присваивается переменной, если она не определена или является пустой строкой ${переменная:?сообщение} Если переменная не определена или является пустой строкой, выводится указанное сообщение ${переменная:+значение} Если переменная инициализирована (определена), вместо нее используется указанное в конструкции значение. Реальное значение переменной не изменяется ${переменная} Если переменная определена, то подставляется ее значение. Скобки используются лишь тогда, если после переменной стоит символ, который может «приклеиться» к имени переменной
Пример.
${1 :? "Не хватает параметра"}
Данное сообщение будет выведено, если сценарий будет запущен без параметров. Если указать хотя бы один параметр, сообщение не будет отображаться на экране.
19.7.8. Функции
Описание функции выглядит так:
имя() { список; }
Пример:
cdir()
{
# изменяем каталог
cd /
}
При выполнении функция не создает нового процесса, а выполняется в среде процесса, содержащего эту функцию. Аргументы функции можно передать ей как обыкновенные параметры при вызове сценария. Функции можно описывать в любом месте сценария, но вызов функции должен осуществляться только после ее описания. Возвращаясь к примеру, модифицируйте функцию:
#!/bin/bash
# файл fn
echo $$
cdir()
{
# изменяем каталог
echo "Х=$Х"
Х=2
echo "Params $0 $# $1 $2"
echo "PID = $$"
return 0
cd $1
}
X=1
echo "X=$X"
cdir /etc
# вызов функции "cd" с параметрами
echo $?
echo "X=$X"
На экране вы увидите примерно следующую информацию:
788
Х=1
Х=1
Params fn l /etc
788
0
Х=2
Проанализируем полученную информацию. Как уже отмечалось, функция не порождает нового процесса, поэтому PID остался равным 788 как до вызова функции, так и во время ее выполнения. Переменная X доступна нашей функции, потому что описана до вызова функции. Функция «видит» значение переменной X, установленное в основном блоке сценария. Затем функция изменяет значение переменной X и передает его в основной блок (Х=2). Функции был передан только один параметр — /etc, вместо второго параметра была отображена пустая строка. Имя файла осталось прежним — fn. Обратите внимание на важный момент: функция сообщила нам много полезной информации об устройстве функций в bash, но не оправдала своего названия — cdir (change dir). Реально изменения каталога не произошло, потому что перед выполнением команды cd была выполнена команда return с кодом завершения 0, которая прервала выполнение функции.
19.7.9. Обработка сигналов и протоколирование
Возможно, вы хотите обеспечить выполнение вашего сценария после выхода пользователя из интерпретатора или выполнить какие-нибудь действия при отключении удаленного пользователя от системы. «Перехватить» сигнал (прерывание) можно с помощью команды trap. Формат команды trap следующий:
trap имя сигналы
где: имя — это имя функции или набор команд, которые должны быть выполнены при получении сигнала;
сигналы — наиболее часто используется перехват сигналов, описанных в табл. 19.42. Полный список сигналов вы найдете в гл. 5.
Сигналы Таблица 19.42
Номер Название Описание 01 SIGHUP Освобождение линии (hangup) 02 SIGINT Прерывание (interrupt) 03 SIGQUIT Выход (quit) 09 SIGKILL Уничтожение процесса (kill). He перехватывается и не игнорируется 15 SIGTERM Программный сигнал завершения
Пример. Игнорирование сигналов 1, 2, 3, 15
trap : 1 2 3 15
: — это пустой оператор, не выполняющий никаких действий.
Рассмотрим, как можно протоколировать работу собственного сценария. Для этого существуют два способа – с помощью команды tee и команды script.
Способ 1:
$LOGFILE=my_log
if ["$LOGGING" != "true"] then export LOGGING="true"; exec $0 I tee $LOGFILE; fi
Способ 2:
$LOGFILE=my_log
if ["$LOGGING" != "true"]; then export LOGGING="true"; exec script $0 $LOGFILE; fi
В первом случае мы устанавливаем флаг протоколирования LOGGING и заново запускаем наш сценарий. При этом перенаправляем весь стандартный вывод команде tee, которая выполнит протоколирование. Второй способ аналогичен первому за исключением того, что мы не будем самостоятельно запускать сценарий — это за нас выполнит команда script. Оба способа можно использовать для протоколирования работы других программ:
script program ~/program.log
20