Присваивание значения переменной при вызове
Переменные называют ключевыми параметрами, поскольку им можно передавать значение по имени при вызове. Рассмотрим пример:
$ 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
Похожие записи
No user прокомментировали сообщение
Оставить комментарий