Книга: 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
x по-прежнему определено
Hello
$
Это означает, что в командном файле нельзя изменить значение переменной, поскольку выполнением командного файла управляет порожденный интерпретатор:
$ echo 'x="Good bye"
Создание shell-файла из двух строк…
> echo $x' >setx
…для определения и печати x
$ cat setx
x есть Hello в исходном интерпретаторе
x="Good Bye"
echo $x
$ echo $x
Hello
$ sh setx
x есть Good Bye в порожденном интерпретаторе…
Good Bye
$ echo $x
…но по-прежнему есть Hello в текущем интерпретаторе
Hello
$
Однако бывают ситуации, когда было бы полезно изменять переменные интерпретатора в командном файле. Очевидным примером является файл, добавляющий новый каталог к вашей переменной PATH
. Поэтому интерпретатор предоставляет команду '.'
(точка), которая выполняет команды из файла в текущем, а не порожденном интерпретаторе. Первоначально это было задумано для удобства пользователей, чтобы они могли повторно выполнять свой файл .profile
, не входя заново в систему, но в принципе это открывает и другие возможности:
$ cat /usr/you/bin/games
Добавим /usr/games к PATH
PATH=$PATH:/usr/games
$ 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
Значение x передается порожденному интерпретатору
Hi
$
(Первоначально присваивания всюду в командной строке передавались команде, но это противоречило dd(1)
.)
Операцию '.'
следует использовать, чтобы навсегда изменить значение переменной, тогда как присваивания в командной строке предназначены для временных изменений. В качестве примера рассмотрим еще раз поиск команд в каталоге /usr/games
, не указанном в вашей переменной PATH
:
$ ls /usr/games | grep fort
Игровая команда fortune
fortune
$ fortune
/usr/games не входит в PATH
fortune: not found
$ echo $PATH
:/usr/you/bin:/bin:/usr/bin
$ PATH=/usr/games fortune
PATH не изменилось.
Позвони в звонок; закрой книгу; задуй свечу.
$ echo $PATH
:/usr/you/bin:/bin:/usr/bin
$ cat /usr/you/bin/games
команда games все еще здесь
$ . games
Сейчас PATH изменилось
$ fortune
Непродуманная оптимизация - источник всех бед - Кнут
$ echo $PATH
:/usr/you/bin:/bin:/usr/bin:/usr/games
$
Можно использовать оба средства в одном командном файле. Вы можете несколько видоизменить команду games
для запуска одной игровой программы без изменения переменной PATH
или постоянно переопределять PATH
, чтобы она включала /usr/games
:
$ cat /usr/you/bin/games
Обратите внимание на $*
PATH=$PATH:/usr/games $*
$ cx /usr/you/bin/games
/usr/games не входит
$ echo $PATH
:/usr/you/bin:/bin:/usr/bin
$ 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
x доступно в порожденном интерпретаторе
Hello
$ x='Good Bye'
Изменим значение x
$ echo $x
Выйдем из порожденного интерпретатора
Good Bye
$ ctl-d
$
Снова в исходном интерпретаторе
$ echo $x
x по-прежнему Hello
Hello
$
Семантика команды export
нетривиальна, но по крайней мере для повседневных нужд достаточно придерживаться основного правила: никогда не экспортируйте временные переменные, служащие для краткосрочных целей, и всегда экспортируйте переменные, необходимые вам во всех порожденных интерпретаторах (включая, например, интерпретаторы, запускаемые командой !
редактора ed
). Поэтому переменные, имеющие специальное значение для интерпретатора, такие, как PATH
и НОМЕ
, следует экспортировать.
Упражнение 3.13
Почему в значение переменной PATH
всегда включается текущий каталог? Куда его нужно поместить?
- 3.1 Структура командной строки
- 3.2 Метасимволы
- 3.3 Создание новых команд
- 3.4 Аргументы и параметры команд
- 3.5 Результат выполнения программы в качестве аргумента
- 3.6 Переменные языка shell
- 3.7 Еще раз о переключении ввода-вывода
- 3.8 Циклы в shell-программах
- 3.9 Программа bundle: соберем все воедино
- 3.10 Для чего нужно программировать на языке shell!
- Глава 3 Возможности интерпретатора shell
- Синтаксис языка Bourne shell
- Указания компилятору языка Си
- Начала языка ActionScript
- Глава 4. Переменные и параметры. Введение.
- Системные переменные ROWS_AFFECTED, GDSCODE, SQLCODE, TRANSACTIONJD, CONNECTIONJD
- 1.2.3. Константы, переменные и типы
- Реализация языка SQL
- Дальнейшее развитие языка SQL
- 1. Оператор Select – базовый оператор языка структурированных запросов
- Компилятор языка С
- 22.3.3. Переменные окружения