Книга: UNIX — универсальная среда программирования

3.6 Переменные языка shell

3.6 Переменные языка shell

Подобно большинству языков программирования, shell имеет переменные, которые на программистском жаргоне называются параметрами. Такие строки, как $1, являются позиционными параметрами-переменными, хранящими аргументы командного файла. Цифра показывает положение параметра в командной строке. Ранее мы имели дело с другими переменными языка shell: PATH — список каталогов, в которых происходит поиск команд, НОМЕ — ваш начальный каталог и т.д. В отличие от переменных в обычном языке переменные, задающие позиционные параметры, не могут быть изменены; хотя PATH представляет собой переменную со значением $PATH, нет переменной 1 со значением $1, т.е. $1 — это не что иное, как компактное обозначение первого аргумента.

Если забыть о позиционных параметрах, переменные языка shell можно создавать, выбирать и изменять. Например,

$ PATH=:/bin:/usr/bin

означает присваивание, изменяющее список каталогов в процессе поиска. До и после знака равенства не должно быть пробелов. Присваиваемое значение должно выражаться одним словом, и его следует взять в кавычки, если оно содержит метасимволы, которые не нужно обрабатывать. Значение переменной выбирается, если предварить имя знаком доллара:

$ PATH=$PATH:/usr/games
$ echo $PATH
:/usr/you/bin:/bin:/usr/bin:/usr/games
$ PATH=:/usr/you/bin:/bin:/usr/bin
Восстановим значение

$

He все переменные имеют специальное значение для интерпретатора. Можно создавать новые переменные, присваивая им значения. По традиции переменные, имеющие специальное значение, обозначаются прописными буквами, а обычные переменные — строчными. Типичным примером использования переменных является хранение в них длинных строк, таких, как имена файлов:

$ pwd
/usr/you/bin
$ dir=`pwd`       
Запомним, где находимся

$ cd /usr/mary/bin Перейдем в другое место

$ ln $dir/cx .     Используем переменную в имени файла

$ ...              Поработаем некоторое время

$ cd $dir          Вернемся

$ pwd
/usr/you/bin
$

Встроенная в интерпретатор команда set показывает значения всех определенных вами переменных. Для просмотра одной или двух переменных более подходит команда echo:

$ set
HOME=/usr/you
IFS=
PATH=:/usr/you/bin:/bin/:/usr/bin
PS1=$
PS2=>
dir=/usr/you/bin
$ echo $dir
/usr/you/bin
$

Значение переменной связано с той копией интерпретатора, который создал ее, и автоматически не передается процессам — потомкам интерпретатора.

$ x=Hello Создание x

$ sh      Новый shell

$ echo $x Происходит только перевод строки,

 x не определено в порожденном интерпретаторе

$ ctl-d   Возврат в исходный интерпретатор

$ echo $x
Hello    
x по-прежнему определено

$

Это означает, что в командном файле нельзя изменить значение переменной, поскольку выполнением командного файла управляет порожденный интерпретатор:

$ echo 'x="Good bye" Создание shell-файла из двух строк…

> echo $x' >setx     …для определения и печати x

$ cat setx
x="Good Bye"
echo $x
$ echo $x
Hello               
x есть Hello в исходном интерпретаторе

$ sh setx
Good Bye            
x есть Good Bye в порожденном интерпретаторе…

$ echo $x
Hello               
…но по-прежнему есть Hello в текущем интерпретаторе

$

Однако бывают ситуации, когда было бы полезно изменять переменные интерпретатора в командном файле. Очевидным примером является файл, добавляющий новый каталог к вашей переменной PATH. Поэтому интерпретатор предоставляет команду '.' (точка), которая выполняет команды из файла в текущем, а не порожденном интерпретаторе. Первоначально это было задумано для удобства пользователей, чтобы они могли повторно выполнять свой файл .profile, не входя заново в систему, но в принципе это открывает и другие возможности:

$ cat /usr/you/bin/games
PATH=$PATH:/usr/games
Добавим /usr/games к PATH

$ echo $PATH
:/usr/you/bin:/bin:/usr/bin
$ . games
$ echo $PATH
:/usr/you/bin:/bin:/usr/bin:/usr/games
$

Поиск файла для команды '.' осуществляется с помощью переменной PATH, так что его можно поместить в ваш каталог bin.

Когда используется команда '.', только условно можно считать, что выполняется командный файл. Файл не "выполняется" в обычном смысле этого слова. Команды из него запускаются, как если бы вы ввели их в диалоговом режиме: стандартный входной поток интерпретатора временно переключается на файл. Поскольку файл читается, не нужно иметь право на его выполнение. Другое отличие состоит в том, что файл не получает аргументов командной строки; $1, $2 и т.д. являются пустыми строками. Было бы неплохо, если бы аргументы передавались, но этого не происходит.

Есть еще один способ установить значение переменной в порожденном интерпретаторе — присвоить его явно в командной строке перед самой командой:

$ echo 'echo $x' >echox
$ cx echox
$ echo $x
Hello
Как и прежде

x не определено в порожденном интерпретаторе

$ x=Hi echox
Hi    
Значение x передается порожденному интерпретатору

$

(Первоначально присваивания всюду в командной строке передавались команде, но это противоречило dd(1).)

Операцию '.' следует использовать, чтобы навсегда изменить значение переменной, тогда как присваивания в командной строке предназначены для временных изменений. В качестве примера рассмотрим еще раз поиск команд в каталоге /usr/games, не указанном в вашей переменной PATH:

$ ls /usr/games | grep fort
fortune
Игровая команда fortune

$ fortune
fortune: not found
$ echo $PATH
:/usr/you/bin:/bin:/usr/bin
/usr/games не входит в PATH

$ PATH=/usr/games fortune
Позвони в звонок; закрой книгу; задуй свечу.
$ echo $PATH
:/usr/you/bin:/bin:/usr/bin
PATH не изменилось.

$ cat /usr/you/bin/games    команда games все еще здесь

$ . games
$ fortune
Непродуманная оптимизация - источник всех бед - Кнут
$ echo $PATH
:/usr/you/bin:/bin:/usr/bin:/usr/games
Сейчас PATH изменилось

$

Можно использовать оба средства в одном командном файле. Вы можете несколько видоизменить команду games для запуска одной игровой программы без изменения переменной PATH или постоянно переопределять PATH, чтобы она включала /usr/games:

$ cat /usr/you/bin/games
PATH=$PATH:/usr/games $*   
Обратите внимание на $*

$ cx /usr/you/bin/games
$ echo $PATH
:/usr/you/bin:/bin:/usr/bin
/usr/games не входит

$ games fortune
Готов отдать свою правую руку, чтобы одинаково владеть обеими
$ echo $PATH
:/usr/you/bin:/bin:/usr/bin
Все еще не входит

$ . games
$ echo $PATH
:/usr/you/bin:/bin:/usr/bin:/usr/games
Теперь входит

$ fortune
Тот, кто медлит, иногда спасается
$

При первом обращении к games командный файл выполняется в порожденном интерпретаторе, в котором переменная PATH временно изменена так, чтобы включать каталог /usr/games. В то же время во втором примере файл обрабатывается текущим интерпретатором при значении $*, являющемся пустой строкой, поэтому в строке нет команд и переменная PATH изменяется. Применение команды games в обоих случаях достаточно нетривиально, но в результате получаем удобное и естественное для использования средство.

Для того чтобы значение переменной было доступно в порожденном интерпретаторе, следует использовать команду export языка shell. (Вы можете поразмышлять о том, почему нет возможности экспортировать значение переменной от порожденного интерпретатора к порождающему его.) Приведем один из рассмотренных выше примеров, но теперь с экспортом переменной:

$ x=Hello
$ export x
$ sh          
Новый интерпретатор

$ echo $x
Hello         
x доступно в порожденном интерпретаторе

$ x='Good Bye' Изменим значение x

$ echo $x
Good Bye
$ ctl-d        
Выйдем из порожденного интерпретатора

$              Снова в исходном интерпретаторе

$ echo $x
Hello          
x по-прежнему Hello

$

Семантика команды export нетривиальна, но по крайней мере для повседневных нужд достаточно придерживаться основного правила: никогда не экспортируйте временные переменные, служащие для краткосрочных целей, и всегда экспортируйте переменные, необходимые вам во всех порожденных интерпретаторах (включая, например, интерпретаторы, запускаемые командой ! редактора ed). Поэтому переменные, имеющие специальное значение для интерпретатора, такие, как PATH и НОМЕ, следует экспортировать.

Упражнение 3.13

Почему в значение переменной PATH всегда включается текущий каталог? Куда его нужно поместить?

Оглавление книги


Генерация: 1.069. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз