Компания ALT Linux
Опубликован: 14.12.2004 | Доступ: свободный | Студентов: 12290 / 1477 | Оценка: 4.19 / 3.84 | Длительность: 18:18:00
ISBN: 978-5-9556-0019-1
Лекция 11:

Shell как язык программирования и интегратор

< Лекция 10 || Лекция 11: 12345 || Лекция 12 >

Окружение

Как уже говорилось, многие абстракции языка программирования доработаны в shell так, чтобы служить средством связывания и взаимодействия процессов с системой и друг с другом. Например, абстракция переменная превращается в понятие переменная окружения (environment variable).

Переменная окружения

С одной стороны, переменная окружения - обыкновенная строковая переменная, в которой кто угодно может хранить какие угодно значения; ее даже и описывать не надо:

$ One=U; Three=Zzz
$ echo Example 1: $One $Two $Three
Example 1: U Zzz
$ echo "Example 2: $One $Two $Three"
Example 2: U     Zzz
$ echo 'Example 3: $One $Two $Three'
Example 3: $One $Two $Three

Из первого примера видно, что сам факт присваивания объявляет переменную, а содержимое необъявленной переменной просто считается пустым (напомним, что команда echo выводит все параметры командной строки, разделяя их пробелом). Как показывают второй и третий примеры, подстановка значения переменной (операция $имя_переменной ) продолжает работать, если текст заключен в двойные кавычки, и не работает, если используются одинарные. По договоренности (см. главу 7) закавыченный текст передается как один параметр командной строки. Теперь очевидно, что подстановку shell делает до вызова команды; только этим можно объяснить, почему в первом примере echo выводит один пробел между U и Zzz: она получает четыре параметра ( Example 1: U и Zzz ), разделенные цепочками пробелов.

Несмотря на то что переменные в shell - строкового типа, легко организовать арифметические операции над ними: если содержимое переменной нельзя интерпретировать как число, арифметическая операция завершается с ошибкой. Арифметика встроена почти во все виды командных интерпретаторов (см. "Сравнительную таблицу командных интерпретаторов"), однако, к сожалению, в каждом это сделано слегка по-своему. Наиболее распространена арифметическая подстановка (arithmetic expansion), при которой shell вычисляет строки вида $((арифметическое выражение)). Есть надежда, что арифметическая подстановка с целыми числами в версии современного BSD- sh или Linux- ash будет работать и в остальных командных интерпретаторах гнезда Bourne:

$ A=7; b=3; echo $(($A*$b))
21

В примере, описывающем понятие сценария, мы видели, как в shell устроен механизм передачи параметров командной строки. Все просто: первый параметр попадает внутри сценария в переменную с именем 1 ( подстановка значения этой переменной выглядит как $1 ), второй - в переменную с именем 2 и т. д., пока есть параметры в командной строке. Имя самого сценария передается в переменной 0 в точности так, как мы его вызвали. Количество переданных параметров подставится вместо $# (напомним, что по американской традиции этот символ используется вместо нашего No). Все параметры командной строки подставляются вместо $* или $@ (Какая между этими формами разница? RTFM!)

Столь простая схема передачи параметров - явная доработка языка в сторону средств интеграции: чаще всего небольшой сценарий - это некоторая составная пользовательская команда, связывающая несколько утилит, и чтобы она не была одноразовой, ей наверняка придется уметь обрабатывать командную строку.

Взаимодействие процессов посредством окружения

С другой стороны, переменная окружения - удобное и простое средство управления системой и утилитами. Не один только командный интерпретатор, но и любой процесс в системе имеет так называемое окружение (environment), которое состоит из пар "имя=значение". Окружение создает система, копируя окружение процесса-родителя. Потомок волен делать со своей копией что угодно: изменять значения переменных, заводить новые, удалять и т. п., на окружении родителя это никак не отразится. С переменными окружения можно передавать важную информацию. Отличие от параметров командной строки заключается в том, что переменную достаточно изменить или задать один раз, и пока она изменена, по-другому будет работать и чувствительная к ней утилита. С точки зрения shell его собственные переменные и есть переменные окружения.

Например, утилита ls использует переменную окружения COLUMNS, в которой хранится наибольшая допустимая ширина текста. Можно обычным присваиванием изменить значение этой переменной, и ls примет его к сведению:

$ echo $COLUMNS
$ ls 
Makefile    myscript    uzor.c
fhs-2.2-source.tar.gz   o.ps
$ COLUMNS=60
$ ls
Makefile    o.ps
fhs-2.2-source.tar.gz   uzor.c
myscript

Многие программы используют переменную EDITOR, содержащую имя текстового редактора: если понадобится что-нибудь редактировать, они запустят именно его. Переменная HOME содержит путь к вашему домашнему каталогу, в TERM хранится тип терминала, в LOGNAME - входное имя пользователя. Переменная PATH используется самим shell для поиска утилиты, если имя команды не совпадает с именами ни одной из встроенных команд. В PATH перечислены каталоги, в которых shell будет утилиту искать. Кстати сказать, именно потому, что в переменной PATH обычно нет текущего каталога (путь " ." не включен в список), в одном из приведенных выше примеров мы были вынуждены явно указывать его в виде ./myscript. Помещать " ." в PATH небезопасно: кто может знать, не окажется ли в текущем каталоге исполняемый файл с именем, скажем, ls, и кто может знать, что на самом деле этот файл будет делать при запуске?

Список определяемых переменных окружения велик, его выдает команда set. Желающих узнать, какими переменными окружения пользуется та или иная утилита, мы отсылаем к ее руководству, раздел ENVIRONMENT VARIABLES.

Сам shell пользуется многими переменными окружения. Например, строка-подсказка (то, при виде чего пользователь захочет ввести очередную команду в командной строке) хранится в переменной PS1 (Если есть PS1, то должно быть PS2, а может, и PS3, и PS4. Для чего они? RTFM!). Поначалу в этой переменной хранилась строка " $ " для обычного пользователя и " # " для суперпользователя. Потом решили использовать PS1 для вывода кое-какой полезной информации: имени компьютера, имени пользователя, даты входа в систему или прочей относительно статической информации. Это просто, достаточно написать что-то вроде:

$ echo $PS1
$
$ PS1="`logname`@`hostname`> "
george@book.altlinux.ru> 
george@book.altlinux.ru>

Однако статическая информация - не самое полезное в работе. Гораздо интереснее держать в PS1, скажем, путь к текущему каталогу или точное время. Сменился текущий каталог - изменилась PS1. Сам shell изменять PS1 не будет, значит, надо его доработать. Первый способ доработки - научить его выполнять определенную последовательность команд перед тем, как выводить очередную подсказку или при смене текущего каталога (так поступили разработчики tcsh: там можно задать специальные функции precmd, cwdcmd и некоторые другие). В эту последовательность команд можно вставить команды изменения PS1. Другой вариант - считать некоторые последовательности символов в PS1 специальными и при выводе подсказки заменять их соответствующими значениями - путем, временем и т. п. Вот, например, как это делается в bash:

bash$ pwd
/usr/share/doc
bash$ PS1="[\A]\u@\h:\w> "
[17:30]george@book:/usr/share/doc> 
[17:30]george@book:/usr/share/doc>

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

$ cat changeA 
A="NewA"
$ A=""
$ . changeA 
$ echo $A  
NewA
< Лекция 10 || Лекция 11: 12345 || Лекция 12 >
Andranik Avakian
Andranik Avakian

41. УК РФ и Комментарии (ст. 273)

М. 2000 г. Издательство: ALT Linux, Институт Логики

Уголовный Кодекс РФ и комментарии к нему?

По ссылке открывается сайт документации Linux, раздел Linux Installation and Getting Started

Сергей Петровский
Сергей Петровский

У Вас написано:

ls -dt1 `grep -il отчет *` | head -1

если знания по шелу мне не изменяют, то должно быть:

ls -dt | `grep -il отчет *` | head -1