Оболочка Bourne


Основные элементы оболочки
Переменные оболочки
Программирование сценариев оболочки
Настройка оболочки
Специальные вопросы

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


В этой главе рассматриваются следующие темы:


Основные элементы оболочки

Вызов

Среда

Опции

Специальные символы

Переменные оболочки

Переменные, определяемые пользователем

Переменные среды

Позиционные переменные или аргументы оболочки

Программирование сценариев оболочки

Проверка условий

Повторение и управление циклами

Настройка оболочки


Основные элементы оболочки


Стефан Борн (Stephen Bourne) написал оболочку Bourne в Bell Laboratories - там, где разрабатывалась сама UNIX. В то время Bell Laboratories была филиалом AT&T. С тех пор многие поставщики компьютеров создали собственные платформно-ориентированные версии UNIX, но, что примечательно, сохранили при этом совместимость с основными элементами оболочки Bourne.

ПРИМЕЧАНИЕ

В большинстве систем UNIX команда $man sh или $man bsh выведет перечень основных элементов оболочки Bourne, а также особенностей характерных для данной версии UNIX. Читателям рекомендуется ознакомиться с используемой версией перед прочтением этой главы после.




Запуск оболочки и среда


Первый уровень вызова имеет место, когда пользователь входит в систему UNIX, и определяется записью пользователя в файле /etc/passwd. Например,

farrawa::411:102:William Farra, Systems Development,x385:/home/farrawa:/bin/bsh

Эта запись (ограниченная символами :) содержит регистрационное имя, зашифрованный пароль (помеченный символом !), числовой идентификатор пользователя, числовой идентификатор группы по умолчанию, поле комментария, начальный каталог и имя программы первичной оболочки. В данном случае это оболочка Bourne. После запуска оболочка выполнит считывание конфигурации системы в файле /etc/profile. При этом могут быть установлены различные переменные среды, такие как PATH, которую оболочка использует для отыскания исполняемых программ, и TERM, определяющая тип подлежащего использованию терминала. Затем оболочка продолжает работу: переходит к начальному каталогу пользователя и считывает локальный файл .profile. И в завершение оболочка выводит на экран приглашение; по умолчанию - $.



ПРИМЕЧАНИЕ

В системах UNIX привилегированный (root) пользователь, может делать в системе все, что угодно, без каких-либо ограничений. Когда таит пользователь входит в систему, он видит знак фунта ($} в качестве приглашения по умолчанию Это служит напоминанием, что с него, как с суперпользователя, сняты некоторые из встроенных элементов защиты и, следовательнор в атом режиме требуется дополнительная осторожность Поскольку суперпользователь может выполнять запись в любой каталог и удалять любые файлы, права доступа к файлам не применяются Как правило, привилегированный вход в систему используется только для системного администрирования и для добавления или | удаления пользователей, Настоятельно рекомендуется предоставлять привилегированный доступ только очень опытным пользователям UNIX



Опции запуска оболочки


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

Ниже приводится перечень опций оболочки Bourne, поддерживаемых в большинстве версий UNIX.

-а Помечает все переменные для экспорта.

-с "строка" Команды считываются из строки.

-е Неинтерактивный режим.

-f Отключает генерирование имен файлов оболочкой.

-h Отыскивает и запоминает определения функций.

-i Интерактивный режим.

-k Помещает аргументы в среду команды.

-n Считывает команды, но не выполняет их.

-r Ограниченный режим.

-s Команды считываются из стандартного устройства ввода.

-t Выполняется одна команда, после чего оболочка осуществляет выход.

-u Подстановка неинициализированных переменных является ошибкой.

-у Многословный режим, отображающий строки ввода оболочки.

-х Режим трассировки, отображающий команды и их выполнение.

Многие из этих опций могут объединяться в комбинации. Для других это явно невозможно, например для -е, устанавливающей неинтерактивный режим, и -i, которая устанавливает интерактивный режим. При наличии некоторого опыта, опции предоставляют пользователю множество альтернатив в создании или изменении среды оболочки.


Ограниченная оболочка


bsh-r ИЛИ /bin/rsh ИЛИ /usr/bin/rsh.

В зависимости от версии UNIX, какая-то из этих трех команд вызовет оболочку Bourne в ограниченном режиме. При установке этой опции пользователь не может менять каталоги (cd), изменять переменную PATH, указывать полный путь команды или перенаправлять вывод. Тем самым обеспечивается дополнительный уровень управления и безопасности системы. Как правило, эта опция используется для пользователей приложений, которые никогда не видят приглашения оболочки, и для бюджетов удаленного доступа, где применение мер безопасности обязательно. Обычно пользователь с ограниченными правами с момента входа в систему помещается в каталог, в котором у него нет права на запись. Отсутствие права на запись в этом каталоге не означает, что пользователь вообще не имеет права осуществлять запись где-либо. Просто пользователь не может менять каталоги или указывать пути в командах. Кроме того, он не может писать сценарии оболочки и запускать их в своем рабочем каталоге.


ПРИМЕЧАНИЕ

Если ограниченная оболочка вызывает неограниченную для передачи ей команд, ограничения могут быть обойдены Это также верно и б том случае, если пользователь может вызвать неограниченную оболочку непосредственно. Помните, что такие программыр как vl и more, разрешают пользователям выполнять команды Если вызываемая команда - sh, это также приведет к обходу ограничений.



Изменение опций оболочки с помощью команды set


Как только появляется командное приглашение $, пользователь может менять среду оболочки, устанавливая или исключая опции с помощью команды set. Для включения опции необходимо использовать - (дефис) и букву опции. Для отключения опции используется + (знак плюса) и буква опции. Большинство систем UNIX допускают включение и отключение опций а, е, f, h, k, n, u, v и х. Взгляните на следующий пример:

$set -xv

В нем активизируется режим трассировки в оболочке, чтобы все команды и подстановки выводились на дисплей. Кроме того, отображается строка, вводимая в оболочку.

$set +u

Эта команда отключает проверку ошибок в виде неинициализированных переменных при выполнении подстановок. Для отображения установленных опций оболочки введите следующее:

$echo $-

is

Этот пример показывает, что оболочка находится в интерактивном режиме и принимает команды из стандартного ввода. Включение и отключение опций очень удобно при отладке сценариев оболочки или при тестировании специфичной среды оболочки.


Пользовательский файл начального запуска оболочки: .profile


В начальном каталоге каждого пользователя оболочки Bourne существует файл .profile. В этом каталоге системный администратор или пользователь (если ему предоставлено право на запись) может выполнять долговременные изменения среды оболочки. Для добавления каталога /sql/bin к существующему пути поиска исполняемых программ в файле .profile достаточно добавить следующую строку:

РАТН=$РАТН:/sql/bin ; export PATH

При наличии этой строки в файле .profile с момента входа пользователя в систему каталог /sql/bin просматривается каждый раз, когда необходимо отыскать исполняемую команду. К примеру, для создания переменной, содержащей данные среды для прикладных программ, необходимо выполнить аналогичную процедуру.


Переменные среды оболочки


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


CDPATH Содержит путь (пути) поиска для команды cd.

HOME Содержит начальный каталог пользователя.

IFS Внутренние разделители полей - обычно, пробел, табуляция и новая строка.

MAIL Путь для специального файла (почтового ящика), используемого электронной почтой UNIX.

PATH Содержит путь (пути) поиска команд и исполняемых программ.

PS1 Первичная строка приглашения, по умолчанию - $.

PS2 Вторичная строка приглашения, по умолчанию - >.

TERM Тип терминала, подлежащего использованию.
Если ограниченный режим не установлен, эти переменные могут быть изменены, чтобы соответствовать различным потребностям пользователя. Например, для изменения приглашения введите следующее:

$PSl="your wish:" ; export PS1

Теперь вместо $ в качестве приглашения отображается your wish:. Для обратного изменения введите:

$PSl="$"; export PS1

Для отображения значения (значений) любой конкретной переменной, введите команду echo. за которой следуют пробел, символ $ и имя переменной.

$Echo $MAIL

/usr/spool/mail/(user id}

При изменении переменных среды необходимо соблюдать осторожность. Некорректное изменение переменных среды оболочки может привести либо к невозможности функционирования команд, либо к их неправильному функционированию. В таком случае рекомендуется выйти из системы, а затем войти снова. Наличие опыта обращения с этими переменными увеличит степень управления средой оболочки. И еще, существует набор переменных оболочки, идентифицируемых специальными символами. Эти символы подробно освещается в следующем разделе данной главы.


Специальные символы и их значения


Оболочка Bourne использует многие неарифметические символы для определения специальных свойств оболочки. Большинство из этих свойств можно отнести к четырем основным категориям: имена специальных переменных, генерация имен файлов, управление данными/программами и управление строковыми константами / еsс -последовательностями. Хотя на первый взгляд подобный синтаксис может показаться непонятной, она предоставляет среде оболочки возможность выполнения сложных функций при минимальном объеме кода.


Специальные символы для имен переменных оболочки


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

$# Количество аргументов, передаваемых оболочке.

$- Флаги, передаваемые оболочке при вызове или с помощью команды set.

$? Значение состояния, возвращенное последней командой.

$$ Идентификатор процесса (PID) текущей оболочки.

$! Идентификатор последнего дочернего процесса.

$@ Все аргументы, каждый из которых заключен в двойные кавычки.

$* Все аргументы, заключенные в общие двойные кавычки.

$л Значения позиционных аргументов, где л - позиция.

$0 Имя текущей оболочки.

Для отображения количества аргументов, переданных оболочке, введите следующее:

$echo $#

0

Этот пример показывает, что при вызове оболочке не был передан ни один аргумент. Эти переменные особенно удобны при написании сценария оболочки, что описывается в этой главе далее в разделе "Позиционные переменные, или аргументы оболочки".


Специальные символы для генерации имен файлов


Оболочка Bourne использует специальные символы или метасимволы для указания шаблона, соответствующего существующим именам файлов. К этим символам относятся следующие:

- * Соответствует любой строке или части строки

- ? Соответствует любому одиночному символу


[-,!] Диапазон, список или отсутствие соответствия

Чтобы установить соответствие с файлами, начинающимися с f, введите следующее:

$1s f*

Для выбора файлов, имеющих префикс invoice, любой средний символ и суффикс dat, введите следующую строку:

$ls invoice?dai-

Для выбора файлов, имена которых начинаются с букв а-е, введите следующее:

$ls [а-е]*

Для выбора файлов, начинающихся с букв а, с и е, введите:

$ls [а,с,е]*

Для выбора файлов, в имени которых буква m отсутствует, необходимо ввести следующую команду:

$ls [!m]*



ПРИМЕЧАНИЕ

Чтобы символ ! мог использоваться в качестве символа логического отрицания, он должен быть первым после левой скобки [



Специальные символы для управления данными/программами


Оболочка Bourne использует специальные символы для управлением потоком данных и выполнением С помощью этих символов пользователь может переслать обычный экранный вывод в файл, или устройство, или на вход другой команды Кроме того, эти символы предоставляют пользователю возможность последовательно или независимо запускать из командной строки несколько команд Перечень этих символов приводится ниже

- >(filе) Перенаправляет вывод в файл.


>>(file) Перенаправляет и дописывает вывод в конец файла.

<(file) Перенаправляет стандартный ввод из файла.

; Разделяет команды.

| Направляет вывод из стандартного устройства вывода на стандартное устройство ввода.

& Помещается в конце команды для выполнения в фоновом режиме.

" Подстановка команды, перенаправляет вывод в качестве аргументов.
Существует много способов использования этих символов управления; они описываются далее в этой главе в разделе "Ввод простых команд".


Специальные символы для определения строк и Escape-символ (символ отмены)


Для создания единой строки данных оболочка Bourne использует одиночные (' ') и двойные (" ") кавычки, заключающие в себя специальные символы или разделенные пробелами слова. Основное различие между одиночными и двойными кавычками в том, что при использовании двойных кавычек активны подстановка переменных и команд и символ Escape.

$echo "$HOME $PATH" $/u/farrawa /bin:/etc:/usr/bin:

В этом примере значения $НОМЕ и $РАТН объединяются для создания единой строки.

$echo '$HOME $РАТН'

$$НОМЕ $РАТН

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

$echo $HOME $РАТН

$НОМЕ /bin:/etc:/usr/bin:

В этом примере только $ из $НОМЕ воспринимается как текстр а значение переменной оболочкой отменяется; $РАТН по-прежнему интерпретируется как переменная.


Как оболочка интерпретирует команды


Первое впечатление, которое у большинства людей складывается об оболочке Bourne, - она интерактивна. После входа в систему и просмотра сообщений системного администратора пользователь видит приглашение оболочки. Для всех пользователей, кроме привилегированных, приглашением интерактивной оболочки Bourne по умолчанию является знак доллара ($). Если отображается знак доллара ($), интерактивная оболочка готова к чтению строки ввода, к последующей ее интерпретации.

Оболочка воспринимает строку ввода в виде строки символов, ограниченной символом новой строки, который обычно вводится нажатием клавиши Enter на клавиатуре. Длина строки ввода никак не связана с шириной дисплея компьютера. Встречая символ новой строки, оболочка начинает интерпретировать строку.


Ввод простых команд


Наиболее распространенная форма ввода - простая команда, в которой за именем команды следует любое количество аргументов. В примере

$ ls. filel file2 file3

ls - команда, а filel, file2 и file3 - аргументы. Командой является любая исполняемая программа UNIX. Именно команда, а не оболочка интерпретирует аргументы. Многие, но, конечно, не все, команды UNIX имеют следующую форму:

$ command -options filenames

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


Подстановка данных из переменных


Во многих из ранее приведенных в этой главе примеров использовались переменные в командной строке. Встречаясь со знаком доллара ($) (но не в кавычках и не после символа отмены), оболочка интерпретирует последующий текст в качестве имени переменной. Независимо от того, является ли переменная переменной среды или определяемой пользователем, хранящиеся в ней данные подставляются в командную строку. Например, команда

$ ls $HOME

выводит содержание начального каталога пользователя, независимо от того, какой каталог в настоящий момент является текущим рабочим. HOME - это переменная среды. Переменные подробней описываются в следующем разделе этой главы. Как и в случае подстановки имени файла, команда Is видит только результат подстановки, а не имя переменной.

Подстановка имени переменной может быть выполнена в любом месте командной строки, в том числе для имени самой команды. Например,

$ dir=ls

$ $dir f*

filel filela form

В этом примере показывается, что оболочка выполняет свои подстановки до определения того, какие команды нужно выполнить.


Перенаправление ввода и вывода


Когда оболочка встречается с символом перенаправления ввода (<) или вывода (>), аргумент, следующий за символом перенаправления, пересылается субоболочке, которая управляет исполнением команды. Когда команда открывает перенаправленный файл ввода или вывода, ввод или вывод перенаправляется в этот файл.

$ ls -l >dirfile

В этом примере единственным аргументом, переданным команде ls, является опция -1. Имя файла dirfile пересылается субоболочке, которая управляет исполнением команды ls. Для добавления вывода к существующему файлу используется (>>).

$ ls -l /tmp >> dirfile

В этом примере список файлов считывается из каталога /tmp и дописывается к концу локального файла dirfile.


Ввод нескольких команд в одной строке


Обычно оболочка интерпретирует первое слово ввода в качестве имени команды, а остальные - в качестве аргументов этой команды. Специальный символ оболочки - точка с запятой (;) - указывает оболочке, что текст предыдущей команды заканчивается и что следующий является новой командой. Например, командная строка

$ who -H; df -v; ps -e

эквивалентна следующему:

$ who -H

$ df -v

$ ps -e

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


Связывание нескольких команд посредством каналов


Одно из наиболее мощных свойств оболочки Bourne - ее способность использовать стандартный вывод одной команды в качестве стандартного ввода другой. Это выполняется с помощью символа канала, |. Когда оболочка встречает этот символ, она выполняет предшествующую ему команду, а затем создает связь со стандартным вводом следующей команды в командной строке. Например,

$who | grep fred

Эта команда принимает от команды who список зарегистрированных пользователей и ищет в нем строку fred, используя команду grep. Тем самым вывод создается только в том случае, если в системе зарегистрирован пользователь fred.

$ls -ls | sort -nr | pg

Эта команда создает список файлов текущего каталога с размером блока (файла) в качестве первого элемента данных каждой строки. Затем команда упорядочивает список в порядке уменьшения номеров и, наконец, постранично выводит результаты на экран. В результате выводится список всех файлов, упорядоченный по размеру, с наибольшим файлом в верхней части. Это удобно при попытке определить, куда исчезло свободное дисковое пространство. Любая команда UNIX, воспринимающая информацию со стандартного устройства ввода и направляющая результаты своего выполнения на стандартное устройство вывода, может быть связана с помощью канала с другой командой.


Ввод команд для выполнения процесса в фоновом режиме


Для максимального использования способности UNIX работать в многозадачном режиме оболочка допускает выполнение команд в фоновом режиме. Это выполняется путем помещения символа амперсанда & в конце команды.

Например,

$find / -name "ledger" -print > find.results 2>/dev/null &

Эта командная строка осуществляет поиск файла ledger во всей файловой системе, пересылает вывод команды find в локальный файл find.results, подавляет сообщения об ошибках и обрабатывает эту команду независимо от текущей оболочки (в фоновом режиме).

$wc -1 < chapters.txt > chapters.wc1 2> chapters.err &

В этом примере команда we принимает данные из файла chapters.txt, пересылает его вывод (счетчик строк) в файл chapter5.wcl, пересылает ошибки в файл chapter5.err и осуществляет выполнение в фоновом режиме.

ПРИМЕЧАНИЕ

Если пользователь, для которого какие-либо процессы выполняются в фоновом режиме, выходит из системы, большинство систем UNIX будет прерывать эти процессы. Кроме того, при вводе команды для процесса, выполняющегося в фоновом режиме, оболочка отобразит соответствующий вывод и идентификатор процесса (РID).




Подстановка в командной строке результатов выполнения команд


Иногда удобно передать вывод или результаты выполнения команды в качестве аргументов другой команды Это делается с помощью специального символа оболочки ~ символа обратных кавычек (`) Эти символы используются парами. Когда оболочка встречается с парой символов обратных кавычек, она выполняет команду, заключенную в кавычки и подставляет вывод этой команды в исходную командную строку Наиболее часто этот метод используется для сохранения результатов выполнения команд в переменных. Например, для сохранения пятизначной даты юлианского календаря в переменной можно воспользоваться следующей командой

$ j ulian= `' date '+%y%j'`

Символы обратных кавычек приводят к тому, что команда date выполняется до присвоения переменной Символы обратных кавычек исключительно удобны при выполнении арифметических действий с переменными оболочки, для получения дополнительной информации обратитесь к разделу "Программирование оболочки" ниже в этой главе.


Переменные оболочки


В алгебре переменные - это символы, которым может быть присвоено какое-либо значение. Согласно компьютерной технологии переменные - это символические имена, которым соответствует какое-либо значение. Выше в этой главе было показано, как переменная HOME означала имя начального каталога пользователя. При вводе команды изменения каталога, cd, без аргумента осуществляет переход к начальному каталогу. Неужели программа типа cd знает расположение начального каталога каждого пользователя? Конечно нет; она просто знает, что нужно искать переменную, в данном случае HOME, в которой содержится начальный каталог.

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


Хранение данных, или Переменные, определяемые пользователем


Как следует из названия, определяемые пользователем переменные предназначены для того, чтобы хранить любые данные. Имена переменных состоят из алфавитно- цифровых символов и символа подчеркивания, с тем ограничением, что они не должны начинаться с цифр 0-9. Подобно всем именам UNIX, имена переменных чувствительны к регистру. Имена переменных принимают значения, которые записаны в командной строке слева от знака равенства (=). Например, в приведенных ниже строках переменная COUNT принимает значение 1, а переменная NAME - значение Stephanie:

$ COUNT=1

$ Name=Stephanie

СОВЕТ

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






Для повторного вызова значения переменной, необходимо перед именем переменной использовать знак доллара ($)

$ NAME=John

$ echo Hello $NAME

Hello John

Можно также присваивать значения одних переменных другим переменным, как показано в следующем примере

$ JOHN=John

$ NAME=$JOHN

$ echo Goodbye $NAME

Goodbye John

Иногда требуется объединение данных переменной с другими символами для образования новых слов, как в следующем примере

$ SUN=Sun

$ MON-Mon

$ TUE=Tuea

$ WED=Wednes

$ HTU=Thurs

$ FRI=Fri

$ SAT=Satur

$ WEEK=$SAT

$ echo Today is $WEEKday

Today is

$

Что происходит в данном случае? Вспомните, что когда интерпретатор оболочки встречается со знаком доллара ($), он интерпретирует все символы вплоть до следующего пробела в качестве имени переменной, в данном случае - WEEKday

Можно предотвратить интерпретацию всей строки, заключив имя переменной в фигурные скобки ({ }), как показано ниже-

$ echo Today is ${WEEK}day

Today is Saturday

$

В одной строке можно присвоить более одной переменной, разделив их пробелами

$ х=х Y=y

Присвоение переменной выполняется справа налево.

$ x=$y Y=y

$ echo $X

У

$ Z=z Y=$Z

$ echo $Y

$

Можно заметить, что при ссылке на инициализированную переменную или на переменную, которая вообще не была определена, оболочка выдает не ошибку, а нулевое значение

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

$ z=hello

$ echo $Z

hello

$ unset Z

$ echo $Z

$


Условная подстановка переменных


Наиболее распространенный способ получения значения переменной - использование знака доллара ($) в качестве префикса имени переменной, в результате чего в указанном месте подставляется значение переменной. В случае оболочки Bourne можно обеспечить, чтобы подстановка переменной имела место только при выполнении определенных условий. Это называется условной подстановкой переменной. Условные подстановки переменных всегда заключаются в фигурные скобки ({ }).


Подстановка значений переменных по умолчанию


Как было показано ранее, при ссылке на переменную, которая не была ранее установлена, подставляется нулевое значение. Оболочка Bourne позволяет устанавливать значения по умолчанию для подстановки переменных, используя форму:

${variable:-value}

где variable - имя переменной, a value - подстановка по умолчанию. Например,

$ echo Hello $UNAME

Hello

$ echo Hello ${UNAME:-there}

Hello there

$ echo $UNAME

$

$ UNAME=John

$ echo Hello ${UNAME: -there}

Hello John

$

Как видно из этого примера, при использовании этого типа подстановки переменных, значение по умолчанию подставляется в командную строку, но значение переменной при этом не меняется. Другая конструкция подстановки не только обеспечивает подстановку значения по умолчанию, но и присваивает его переменной. Эта подстановка имеет форму

${variable:= } что приводит к присвоению переменной variable значения value после выполнения подстановки. Например,

$ echo Hello $UNAME

Hello

$ echo Hello ${UNAME:=there}

Hello there

$ echo $UNAME

there

$ UNAME=John

$ echo Hello ${UNAME: -there}

Hello John

$

Значение подстановки не обязательно должно быть символьным; оно может быть командой, заключенной в символы обратных кавычек:

USERID={$MYDIR:-'pwd'}

Третий тип подстановки переменных обеспечивает подстановку указанного значения, если переменная уже инициализирована, как показано ниже:

${variable:+value}

Если переменная variable инициализирована, то подставляется значение value, если переменная variable не инициализирована, подстановка не выполняется. Например,

$ ERROPT=A

$ echo ${ERROPT:+"Error Tracking is Active"}

Error Tracking is Active

$ERROPT=

$ echo ${ERROPT:+"Error Tracking is Active"}

$


Условная подстановка переменных с проверкой ошибок


Еще один метод подстановки переменных позволяет выполнять проверку ошибок во время подстановки переменных:

${variable:?message)

Если переменная variable уже инициализирована, ее значение подставляется; если нет -записываете в стандартный файл ошибок сообщение message. Если подстановка выполняется в программе оболочки программа немедленно прерывается. Например,

$ UNAME=

$ echo ${ UNAME:?"UNAME has not been set"}

UNAME has not been set

$ UNAME=Stephanie

$ echo ${UNAME:?"UNAME has not been set"}

Stephanie

$

Если никакое сообщение не указано, оболочка отображает сообщение по умолчанию, как в следующее примере:

$ UNAME=

$ echo ${UNAME:?}

sh: UNAME: parameter null or not set

$


Позиционные переменные, или аргументы оболочки


Читатели, вероятно, помнят, что когда интерпретатор командной строки оболочки обрабатывает строку ввода, первое слово командной строки считается исполняемым файлом, а остальные слова передаются команде в качестве аргументов. Если исполняемый файл является программой оболочки, аргументы пере даются ей в качестве позиционных переменных. Первый передаваемый программе аргумент присваивается переменной $1, второй - переменной $2 и т.д. до $9. Обратите внимание, что именами переменных в дей ствительности являются цифры 1-9; знак доллара, как всегда, служит специальным символом, вызываю щим подстановку переменной.

Позиционная переменная $0 всегда содержит имя исполняемой программы. Позиционные переменные подробней рассматриваются далее в этой главе в разделе "Программирование оболочки".


Предотвращение изменения переменных


Если переменной присвоено значение и нужно обеспечить, чтобы это значение впоследствии не из менялось, можно обозначить эту переменную как read-only (только для чтения) с помощью следующей команды

readonly variable

С этого момента переменой variable невозможно присвоить новое значение. Тем самым предотвращается случайное изменение переменной.


Обеспечение доступности переменных для субоболочек с помощью команды export


Когда оболочка выполняет программу, она создает новую среду, в которой будет выполняться программа Эта среда называется субоболочкой. В оболочке Bourne переменные считаются локальными; иначе говоря они недоступны вне оболочки, в которой им были присвоены значения. Можно сделать переменную доступной для любых выполняющихся субоболочек, экспортировав ее с помощью команды export. Однако невозможно обеспечить доступ к переменным другим пользователям.

Теперь предположим, что пользователь запускает новую оболочку.

Enter Command: sh

$ exit

Enter Command

При запуске новой оболочки отображается приглашение оболочки по умолчанию. Это происходит потому, что присвоение переменной PS1 было выполнено только в текущей оболочке. Чтобы активизировав новое приглашение оболочки в субоболочках, нужно экспортировать его, как в следующем примере.

$ PSl="Enter Command: "

Enter Command: export

Enter Command: sh

Enter Command:

Теперь переменная PS1 является глобальной; она доступна для всех субоболочек. Когда переменная делается глобальной таким способом, она остается глобальной до момента выхода из родительской оболочки. Назначение можно сделать постоянным, включив его в файл .profile (см. раздел "Настройка оболочки").


Программирование сценариев оболочки


В этом разделе будет показано, как объединять команды, чтобы вместе они обеспечивали большие возможности, чем отдельные команды. Читатели познакомятся в командами UNIX, которые полезны в основном в контексте программ оболочки. Читатели также узнают, как заставить программу выполнять функции условно, исходя из определенных пользователем логических проверок, и как заставить части программы повторяться до полного выполнения требуемой функции. Короче говоря, читатели узнают, как использовать обычные инструментальные средства, поставляемые с UNIX, для создания более мощных средств, ориентированных на требующие выполнения задачи.


Что такое программа?


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

$ ls -R / | grep myname | pg

выполняется программирование оболочки; пользователь заставляет компьютер выполнять ряд утилит в определенном порядке, что дает более полезный результат, чем каждая утилита сама по себе.


Простая программа


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

$ cd /usr/home/myname; ls * | cpio -о >/dev/rmt0

Как бьшо показано ранее, при вводе подобной сложной команды выполняется программирование оболочки. Однако программы удобны тем, что могут быть помещены в библиотеку программ и использоваться снова и снова, без необходимости каждый раз выполнять программирование. Программы оболочки не являются исключением. Вместо того чтобы каждый раз вводить длинную команду резервирования, можно сохранить программу в файле backup:

$ cat >backup

cd /usr/home/myname

ls * | cpio -o >dev/rmt0

Ctrl+d

Можно, конечно, воспользоваться любимым текстовым редактором (см. "UNIX для Internet. Энциклопедия пользователя", глава 3 "Редактирование текста с помощью vi и emacs"); в случае создания больших программ оболочки, скорее всего, пользователь так и поступит. Можно ввести команды в одной строке, как это делалось при вводе командной строки, но поскольку команды в программе оболочки (которую иногда называют сценарием оболочки) выполняются последовательно, помещение каждой команды в отдельной строке упрощает чтение программы. Важность создания легко читаемых программ возрастает с увеличением размеров программ.

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

$ sh backup

Программа sh - та же оболочка Bourne, которая была запущена при входе в систему, но при передаче ей имени файла в качестве аргумента, вместо того чтобы стать интерактивной оболочкой, она берет команды из файла.

Другой метод выполнения команд файла backup - превращение самого файла в исполняемую программу. Для этого используется следующая команда:

$ chmod +x backup

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

$ backup

Если команды желательно выполнять таким образом, файл backup должен размещаться в одном из каталогов, указанных в переменной среды $РАТН.


Оболочка как язык программирования


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

Используя эти свойства, можно автоматизировать многие повторяющиеся функции, что, конечно, является назначением любого компьютерного языка.


Использование переменных в программах оболочки


Обычно в программах переменные используются в качестве шаблонов для данных, которые будут доступны во время выполнения программы и могут изменяться от выполнения к выполнению. Давайте рассмотрим программу backup:

cd /usr/home/myname

ls | cpio -o >dev/rmt0

В данном случае подлежащий резервному копированию каталог содержится в программе в виде строковой константы, литерала, то есть постоянного значения. Эта программа полезна для резервного копирования только этого единственного каталога. Использование переменной делает программу более общей:

cd $WORKDIR

ls * | cpio -o >/dev/rmt0

С этим небольшим изменением любой пользователь может использовать программу для выполнения резервного копирования каталога, названного в переменной SWORKDIR, при условии, что эта переменная экспортируется в субоболочки. (См. раздел "Обеспечение доступности переменных для субоболочек с помощью команды export" ранее в этой главе.)


Ввод комментариев в программах оболочки


При написании программ довольно часто программный код, который казался вполне логичным шесть месяцев назад, может показаться довольно запутанным. Хорошие программисты всегда снабжают свои программы комментариями. Комментарии в программы оболочки помещаются с помощью вставки специального символа - знака фунта (#). Когда интерпретатор оболочки встречается со знаком фунта, он считает весь текст до конца строки комментарием.


Выполнение арифметических действий с переменными оболочки


В большинстве языков программирования высокого уровня переменные являются типизированными, т.е. они ограничиваются определенными видами данных, например числами или символами. Переменные оболочки всегда хранятся в виде символов. Для выполнения арифметических действий с переменными оболочки необходимо использовать команду ехрr.

Команда ехрг обрабатывает свои аргументы как математические выражения. Общая форма этой команды выглядит следующим образом:

ехрr ехрr целое операция целое

Поскольку оболочка хранит свои переменные в виде символов, именно программист оболочки должен обеспечить, чтобы целые аргументы команды ехрг действительно были целыми. Ниже приводится перечень допустимых арифметических операций:

- + Суммирует два целых значения.


- Вычитает второе целое значение из первого.

* Умножает два целых значения.

/ Делит первое целое значение на второе.

% Находит модуль (остаток) деления.
$ ехрr 2+1

3

$ ехрr 5-3

2

Если аргумент команды ехрr является переменной, значение переменной подставляется перед вычислением выражения, как в следующем примере:

$ $int=3

$ ехрr $int + 4

7

Необходимо избегать одиночной операции в виде звездочки (*) для выполнения умножения. Если ввести

$ ехрr 4*5

будет получена ошибка, т.к. оболочка встречает звездочку и трактует ее как шаблон имени файла. Правильной формой выражения умножения является

$ ехрr 4 * 5

20

Арифметические выражения можно также объединять, как в следующем примере:

$ ехрr 5+7/3 7

Результат этого примера может показаться странным. Прежде всего следует помнить, что деление и умножение обладают более высоким приоритетом по сравнению со сложением и вычитанием, поэтому сперва выполняется деление 7/3. Поскольку ехрr имеет дело только с целыми числами, результатом деления является 2, который затем прибавляется к 5, что и дает окончательный результат 7. Скобки не распознаются командой ехрr, поэтому порядок следования действий изменяется вручную. Для изменения порядка следования действий можно использовать символы обратных кавычек, как показано ниже:

$ int='expr 5 + 7'

$ ехрr $int / 3

Или же можно было бы пойти по более прямому пути:

$ ехрr 'ехрг 5+7' /З 4


Передача аргументов программам оболочки


Программа может получать данные двумя способами: они либо передаются программе в качестве аргументов при запуске, либо программа получает данные интерактивно. Такие редакторы, как vi, обычно используются в интерактивном режиме, а команды, подобные ls и ехрr, получают свои данные в качестве аргументов. Программы оболочки не составляют исключения. В разделе "Интерактивное считывание данных в программу" показано, как программа оболочки может интерактивно получать свои данные.

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

$ cat >restoreall

cd $WORKDIR

cpio -i </dev/rmt0

Ctrl+d

Программа restoreall перезагружает всю запись, созданную на ленте программой backup. Но что делать, если нужно восстановить с ленты только отдельный файл? Этого можно добиться, передав имя файла в качестве аргумента. Усовершенствованная программа restorel выглядит следующим образом:

# restorel - программа для восстановления отдельного файла

cd $WORKDIR

cpio -i </dev/rmt0

Теперь можно передать программе restorel параметр, представляющий имя файла, подлежащего восстановлению:

$ restorel filel

В этом примере имя файла filel передается программе restorel в качестве первого аргумента. Недостатком программы restorel является то, что для восстановления двух файлов ее нужно запускать дважды.

В качестве заключительного усовершенствования можно использовать переменную $* для передачи программе любого количества аргументов:

# restoreany - программа для восстановления любого числа файлов

cd $WORKDIR

cpio -i $* </dev/rrot0

$ restoreany filel file2 file3

Поскольку переменные оболочки, которым не присвоены значения, всегда возвращают нуль, если программы restorel или restoreany выполняются без каких-либо параметров командной строки, в команду cpio помещается нулевое значение, что приводит к восстановлению всего архива.

Давайте рассмотрим программу, представленную в листинге 9.1; она вычисляет время поездки на определенное расстояние.


Листинг 9.1. Пример программы с двумя параметрами.

# traveltime - программа для вычисления времени,

# которое потребуется для поездки на фиксированное расстояние

# синтаксис: traveltime расстояние миль/час

Х6О=ехрr $1 * 60`

TOTMINUTES= `expr $X60 / $2`

HOURS=`expr $TOTMINUTES / 60'

MINUTES=`expr $TOTMINUTES % 60`

echo "The trip will take $HOURS hours and $MINUTES minutes"


Программа, представленная в листинге 9.1, принимает два позиционные параметра: расстояние в милях и скорость в милях в час. Расстояние передается программе в качестве параметра $1, а скорость - в качестве $2. Обратите внимание, что первая команда в программе умножает расстояние на 60. Поскольку команда ехрr работает только с целыми значениями, она удобна для вычисления времени поездки в минутах. Определяемая пользователем переменная х60 содержит промежуточный результат, который при делении на скорость дает общее время поездки в минутах. Затем, используя деление целых и деление по модулю, определяется количество часов и минут, требующееся для поездки.

Теперь выполним программу traveltime для путешествия на расстояние 90 миль со скоростью 40 миль/час, воспользовавшись следующей командой:

$ traveitime 90 40

The trip will take 2 hours and 15 minutes (Поездка займет 2 часа и 15 минут)


Принятие решений в программах оболочки


Одно из наиболее мощных свойств всех компьютерных языков программирования - их способность принимать решения. Конечно, компьютер не может думать, поэтому решения, принимаемые компьютерными программами, - всего лишь ответ на условия, предусмотренные в программе пользователем. Принятие решений компьютерной программой осуществляется в форме условного выполнения: при определенных условиях выполняется определенный набор команд. В большинстве компьютерных языков это называется конструкцией if-then.


Конструкция if-then


В оболочке Bourne также предусмотрена конструкция if-then. Она имеет следующий синтаксис:

if coiranand_l

then

command_2

command_3

fi

command_4

Читатели, вероятно, помнят, что каждая программа или команда завершается возвратом состояния выхода. Состояние выхода последней завершившейся программы доступно в переменной оболочки $? Оператор if проверяет состояние выхода своей команды. Если эта команда успешна, то все команды, расположенные между операторами then и fi выполняются. В этой программной последовательности команда command_l выполняется всегда, команды command_2 и command_3 выполняются только в том случае, если команда command_1 успешна, и команда command_4 выполняется всегда.

Давайте рассмотрим вариант программы backup, отличающийся тем, что после копирования всех файлов на резервный носитель они удаляются с диска. Давайте назовем программу unload и дадим возможность пользователю указывать каталог, подлежащий выгрузке, как показано в следующем примере:

# unload - программа резервного копирования и последующего удаления файлов

# синтаксис - unload-каталог

cd $1

ls -а | cpio -o >/dev/rmt0

rm *

На первый взгляд кажется, что эта программа будет выполнять требуемое от нее. Но вдруг что-либо случится во время выполнения команды cpio? В данном случае резервный носитель - устройство записи на ленту. Что будет, если оператор не заправит чистую ленту? Команда rm выполнится, уничтожив каталог

прежде, чем он будет сохранен! Конструкция if-then предотвращает подобную катастрофу. Модернизированная программа unload показана в листинге 9.2.


Листинг 9.2. Программа оболочки с проверкой ошибок.

# unload - программа резервирования и удаления файлов

# синтаксис - unload каталог

cd $1

if ls -a | cpio -o >/dev/rmt0 then

rm * fi

В программе, показанной в листинге 9.2, команда rm выполняется только в том случае, если команда cpio успешна. Обратите внимание, что оператор if просматривает состояние выхода последней команды конвейера,


Вывод данных из программ оболочки


Стандартный вывод и вывод ошибок любой команды внутри программы оболочки передаются на стандартное устройство вывода пользователя, вызывавшего программу, если только вывод не перенаправляется из самой программы. В примере, приведенном в листинге 9.2, любые сообщения об ошибке, выводимые командой cpio, должны быть видны пользователю программы. Иногда может потребоваться написание программ, которые должны осуществлять обмен данными с пользователем. В программах оболочки Bourne это обычно делается посредством использования команды echo. Как следует из названия, команда echo просто пересылает свои аргументы на стандартный вывод и добавляет в конце символ новой строки, как показано в следующем примере:

$ echo "Mary had a little lamb"

Mary had a little lamb

Команда echo распознает несколько esc-последовательностей, позволяющих форматировать вывод. Эти символы следующие:


 Возврат на один символ

с Печатает строку без символа новой строки

f Form Feed: осуществляет переход к новой странице на принтере; осуществляет переход к новому экрану на дисплее терминала

Новая строка

Возврат каретки

Осуществляет табуляцию

v Осуществляет табуляцию по вертикали

\ Обратная косая черта

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

$echo "Enter response:с"

Enter response$


Конструкция if-then-else


Распространенная потребность программирования - выполнение одного набора команд, если условие истинно, и другого - если оно ложно. В оболочке Bourne этого эффекта можно добиться, используя конструкцию if-then-else:

if

then

command_2

command_3

else

command_4

command_5

fi

В этой конструкции команда command_1 выполняется всегда. Если command_1 успешна, выполняются команды command_2 и command_3 если нет - команды command_4 и command_5.

Теперь можно усовершенствовать программу unload, чтобы она была более дружественной по отношению к пользователю. Например,


# unload - программа для резервного копирования и последующего удаления файлов

# синтаксис - unload каталог

cd $1

if ls -a | cpio -о >/dev/rmt0 then

rm *

else

echo "A problem has occurred in creating the backup."

echo "The directory will not be erased."

echo "Please check the backup device and try again."

fi

СОВЕТ

Поскольку оболочка игнорирует лишние пробелы в командной строке, опытные программисты используют это для улучшения читабельности своих программ Когда команды выполняются внутри оператора then или е1sе, все команды в конструкции рекомендуется вводить с одинаковым отступом






Проверка условий с помощью команды test


Было показано, как оператор if проверяет состояние выхода своей команды для управления порядком выполнения команд. Но как быть, если нужно проверить другие условия? В программах оболочки часто используется команда test. Она проверяет некоторое условие и возвращает нулевое состояние выхода, если условие истинно, и ненулевое ~ если условие ложно. Это предоставляет оператору if в оболочке Bourne те же возможности, что и в других языках программирования.

Общая форма команды выглядит следующим образом:

test condition

Проверяемые условия могут быть разделены на четыре категории: 1) Строковые операции, проверяющие условие или соотношения символьных строк; 2) Целые соотношения, проверяющие численные соотношения двух целых значений; 3) Файловые операции, проверяющие наличие или состояние файла;

4) Логические операции, которые допускают объединение и/или других условий.


Проверка символьных данных


Ранее было указано, что оболочка Bourne не выполняет приведение типов элементов данных. Каждое слово строки ввода и каждая переменная могут восприниматься как строка символов. Некоторые команды, например, ехрr и test обладают способностью выполнять численные операции со строками, которые могут быть преобразованы в целые значения, но любой элемент данных может обрабатываться только как символьная строка.

Можно сравнить две строки для проверки их эквивалентности. Можно также проверить отдельную строку, чтобы убедиться в наличии у нее значения. В число строковых операций входят следующие:


strl = str2 Истинно, если строка strl равна по длине и содержит те же символы, что и строка str2.

strl != str2 Истинно, если строка strl не совпадает со строкой str2.

-n strl Истинно, если длина строки strl больше 0 (не является нулевой).

-z strl Истинно, если строка strl является нулевой (имеет нулевую длину).

strl Истинно, если строка strl не является нулевой.
Хотя чаще всего test используется в программах оболочки для принятия решений, она является вполне самостоятельной программой, как показано в следующем примере:

$ strl=abcd

$ test $strl = abcd

$ echo $?

0

$

Обратите внимание, что, в отличие от операции присвоения переменной в первой строке этого примера, команда test должна содержать знак равенства, окруженный пробелами. В этом примере оболочка пересылает команде test три аргумента. Строки должны быть эквивалентны как по длине, так и посимвольно.

$ strl="abcd "

$ test "$strl" = abcd

$ echo $?

1

$

В этом примере строка strl содержит пять символов, последний из которых - пробел. Вторая строка в команде test содержит только четыре символа. Операция неэквивалентности возвращает значение true (истинно) во всех тех случаях, когда операция эквивалентности возвращает значение false (ложно).

$ strl=abcd

$ test $strl != abcd

$ echo $?

1

$

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

$ strl=abcd

$ test $strl

$ echo $?

0

$ test -n $strl

$ echo $?

0

$

Различие между двумя командами в этом примере мало заметно, но оно указывает на проблему, которая может возникнуть при использовании команды test, как показано в следующем примере двух различных проверок:

$ strl=" "

$ test $strl

$ echo $?

1

$ test "$strl"

$ echo $?

0

$ test -n $strl

test: argument expected

$ test -n "$strl

$ echo $?

0

В этом примере результатом выполнения первой команды test является false. Почему? Вспомните, что перед обработкой командной строки интерпретатор оболочки выполняет подстановку переменных, а при обработке командной строки удаляет лишние пробелы. Там, где $strl не имеет символов двойных кавычек, пробелы передаются в командную строку и удаляются; если же используются символы двойных кавычек, пробелы передаются команде test. А что происходит в третьей команде test? Когда интерпретатор удаляет пробел, команде test передается только опция -n, которая требует аргумента.


Проверка численных данных


Команда test, подобно команде ехрr, обладает способностью преобразовывать строки в целые числа и выполнять численные операции. В тех случаях, когда команда eхрr выполняет арифметические действия с целыми числами, команда test выполняет операции логического сравнения. Допустимыми операциями логического сравнения являются следующие:

int1 -eq int2 Истинно, если int1 численно равно int2

int1 -ne int2 Истинно, если int1 не равно int2

int1 -gt int2 Истинно, если int1 больше int2

int1 -ge int2 Истинно, если int1 больше или равно int2

int1 -It int2 Истинно, если int1 меньше int2

int1 -le int2 Истинно, если int1 меньше или равно int2

Различие между численной и строковой эквивалентностью показано в следующем примере, где определяются две строки, а затем выполняется их сравнение сперва с использованием численной, а затем строковой эквивалентности.

$ strl=1234

$ str2=01234

$ test $strl = $str2

$ echo $?

1

$ test $Strl -eq $str2

$ echo $?

0

$

Во втором случае строки были преобразованы в целые числа и выяснилось, что они численно эквивалентны, в отличие от исходных.


Проверка файлов


Третий тип условий, который может быть проверен командой test - состояние файлов. Используя в программе команду test, можно определить доступность файла для записи в него и ряд других условий. Все опции проверки файлов возвращают true (истинно) только в том случае, если файл существует. Опциями проверки файлов являются следующие:


-r filenm Истинно, если пользователь обладает правом доступа для чтения

-w filenm Истинно, если пользователь обладает правом доступа для записи

-х filenm Истинно, если пользователь обладает правом доступа для выполнения

-f filenm Истинно, если filenm - обычный файл

-d filenm Истинно, если filenm - каталог

-с filenm Истинно, если filenm - байт-ориентированный файл "b filenm Истинно, если filenm - блок-ориентированный файл

-s filenm Истинно, если размер filenm не равен нулю

-t fnumb Истинно, если устройство, связанное с дескриптором файла jhumb (1 по умолчанию), является терминальным устройством

Объединение и отрицание условий команды test


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

Иногда интерес представляет не то, что является истинным, а то, что не является истинным. Для выяснения этого можно использовать одночленную операцию отрицания, восклицательный знак (!), помещаемый перед любой первичной операцией. Создайте пустой файл и проверьте некоторые из файловых операций, показанных в следующем примере:

$ cat >empty

Ctrl+d

$ test -r empty

$ echo $?

0

$ test -s empty

$ echo $?

1

$ test ! -s empty

$ echo $?

0

$

Первичные выражения в команде test могут быть объединены с операцией логического и (-а) или логического или (-о). При использовании операции -а объединенное выражение истинно тогда и только тогда, когда оба первичные выражения истинны. При использовании операции -о объединенное выражение истинно, если любое из первичных выражений истинно. Давайте используем пустой файл empty из предшествующего примера, чтобы проверить, является ли он доступным для чтения и содержит ли данные:

$ test -r empty -a -s empty

$ echo $?

1

$

Объединенное выражение ложно. Первое выражение истинно, т.к. файл доступен для чтения, но второе выражение ложно, поскольку файл имеет нулевой размер.


Ускоренный метод выполнения проверок


Поскольку команда test является весьма важной частью программирования оболочки, а также чтобы создание программ оболочки больше походило на программы на других языках, оболочка Bourne имеет альтернативный метод использования команды test: все выражение заключается в квадратные скобки ([]).

$ intl=4

$ [ $intl -gt 2 ]

$ echo $?

0

$

Необходимо помнить, что, несмотря на несколько иной вид, в этом примере показана все та же команда test и к ней применимы те же правила.

Используя команду test, можно сделать программу unload из листинга 9.2 более дружественной по отношению к пользователю, а также более надежной, обеспечив ввод в командной строке допустимого имени каталога. Измененная версия программы показана в листинге 9.3.


Листинг 9.3. Программа, использующая команду test для проверки ошибок.

# unload - программа резервного копирования и последующего удаления файлов

# синтаксис - unload каталог

# проверка аргументов

if [ $# -nе 1 ]

then

echo "usage: unload directory"

exit 1

fi

# проверка допустимых имен каталогов

if [! -d "$1" ]

then

echo "$1 is not a directory"

exit 2

fi

cd $1

Is -a | cpio -o >/dev/rmt0

if [ $? -eq 0 ]

then

rm * else

echo "A problem has occurred in creating the backup."

echo "The directory will not be erased."

echo "Please check the backup device and try again."

exit 3

fi

В измененной программе в листинге 9.3 имеется несколько интересных моментов. Один из них - появление оператора exit. У него два назначения: остановка выполнения любых следующих за ним в программе команд и установка состояния выхода программы. При установке ненулевого состояния выхода последующие программы могут проверить переменную $?, чтобы узнать, успешно ли выполнилась программа unload. Обратите внимание, что для проверки того, является ли аргумент допустимым каталогом, подстановка выполняется внутри двойных кавычек. Использование двойных кавычек предотвращает ошибку команды test при вызове программы с аргументом, содержащим только пробелы; проверка по-прежнему не удается, но пользователь не видит сообщение об ошибке команды test. Еще одно изменение в программе - удаление команды действительного резервирования из оператора if и помещение ее в отдельной строке с последующим применением команды test применительно к состоянию выхода для принятия решения. Хотя использование if для проверки состояния выхода команды резервирования допустимо и, возможно, более эффективно, смысл может ускользать от случайного наблюдателя.

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

$ traveltime 61 60

The trip will take 1 hours and 1 minutes (Поездка займет 1 часов и 1 минут)

Хотя этот ответ и верен, но он может заставить поморщиться преподавателя языка. Чтобы сделать вывод более корректным, можно использовать численную проверку и конструкции if-then-else. Измененная программа показана в листинге 9.4.


Листинг 9.4. Измененная программа traveltime.

# traveltime - программа вычисления времени,

# которое потребуется для поездки на фиксированное расстояние

# синтаксис: traveltime расстояние миль/час

Хб0= `ехрr $1 * 60`

TOTMINUTES=`expr $Х60 / $2'

HOURS='expr $TOTMINUTES / 60'

MINUTES=`expr $TOTMINUTES % 60'

if [ $HOURS -gt 1 ]

then

DISPHRS=hours

else

DISPHRS=hour

fi

if [ $MINUTES -gt 1 ]

then

DISPMIN-minutes

else

DISPMIN=minute

fi

echo "The trip will take $HOURS $DISPHRS c"

if [ $MINUTES -gt 0 ]

then

echo "and $MINUTES $DISPMIN"

else

echo fi

Теперь программа traveltime обеспечивает вывод соответствующих существительных единственного и множественного числа в зависимости от значения времени:

$ traveltime 50 40

The trip will take 1 hour and 15 minutes (Поездка займет 1 час и 15 минут)

$ traveltime 121 60

The trip will take 2 hours and 1 minute (Поездка займет 2 часа и 1 минуту)

$ traveltiroe 120 60

The trip will take 2 hours (Поездка займет 2 часа)

$


Команда Null


Теперь программа unload усовершенствована для восприятия названия каталога из командной строки, для проверки его допустимости и для предоставления пользователю программы больше информации о любых ошибках, которые могут иметь место. Единственное реальное различие между функциями unload и backup заключается в том, что unload удаляет файлы из каталога после того, как тот заархивирован. Может показаться, что простое изменение в unload - удаление оператора rm - должно трансформировать ее в усовершенствованную версию backup. Проблема состоит в том, что команда rm - единственная, которая следует за оператором then, а за каждым таким оператором должна следовать по меньшей мере одна команда. Оболочка Bourne обеспечивает решение этой проблемы с помощью нулевой команды. Нулевая команда, представляемая двоеточием (:), служит шаблоном, назначение которого - выполнение требования наличия какой-либо команды. Для преобразования unload в backup необходимо заменить команду rm нулевой командой и изменить некоторые сообщения.

# backup - программа резервного копирования всех файлов каталога

# синтаксис " backup каталог

# проверка аргументов

if [ $* -nе 1 ]

then

echo "usage: backup directory"

exit 1 fi

# проверка допустимых имен каталогов

if [ ! -d "$1" ]

then

echo "$1 is not a directory"

exit 2 fi

cd $1

ls -a | cpio -o >/dev/rmt0 if [ $? -eq 0 ]

then

:

else

echo "A problem has occurred in creating the backup."

echo "Please check the backup device and try again."


Отображение названия программы


В двух предшествующих примерах отображалось вспомогательное сообщение для пользователя, который не вводил аргументы командной строки.

В этом сообщении название программы отображалось в виде части текстовой строки. Однако, в случае

переименования программы, это сообщение было бы не соответствующим действительности. В оболочке Bourne переменная $0 всегда содержит название программы, введенное из командной строки. Теперь программу можно сделать более общей, как показано в следующем примере:

if [ $# -nе 1 ]

then

echo "usage: $0 directory"

exit 1

fi


Вложенные операторы if и конструкция elif


Часто может потребоваться, чтобы программа делала следующее:

1. Проверяла первичное условие и

А. Если первичное условие истинно, выполняла операцию.

В. Если первичное условие ложно, проверяла вторичное условие.

(1) Если вторичное условие истинно, выполняла другую операцию.

(2) Если вторичное условие ложно, проверяла третье условие.

(а) Если третье условие истинно, выполняла еще одну операцию.

Это можно сделать с помощью вложенных инструкций if-else, как показано в следующей синтаксической конструкции:

if command

then

command

else

if command

then

command

else

if command

then

command

fi

fi

fi

Вложенность удобна, но, вместе с тем, может быть достаточно сложной, особенно в плане верного помещения инструкций fi. Поскольку подобного рода программирование встречается часто, оболочка Bourne имеет специальную конструкцию elif (название является сокращением от else-if), указывающую на продолжение основного оператора if Показанную выше последовательность можно было бы переопределить с помощью операторов elif следующим образом:

if command

then

command

elif command

then command

elif command

then command

fi

Оба эти метода дают один и тот же результат. Следует использовать тот из них, который более удобен для вас.


Интерактивное считывание данных в программу


До сих пор весь ввод в программу осуществлялся пользователями в форме аргументов командной строки. Однако можно также вводить данные в программу, используя оператор read. Синтаксис оператора read следующий:

read varl var2 ... varn

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

В следующем примере оператор read ищет три переменные. Поскольку строка ввода содержит три слова, каждое слово присваивается переменной.

$ read varl var2 var3

Hello my friend

$echo $varl $var2 $var3

Hello my friend

$ echo $varl

Hello

$echo $var2

my

$ echo $var3

friend

$

В следующем примере оператор read ищет три переменные, но строка ввода состоит из четырех слов. В этом случае последние два слова присваиваются третьей переменной.

$ read varl var2 var3

Hello my dear friend

$ echo $varl

Hello

$echo $var2

my

$ echo $var3

dear friend

$

И наконец, в следующем примере строка ввода содержит меньше слов, чем указано переменных в операторе read, поэтому последняя переменная остается неинициализированной.

$ read varl var2 var3

Hello friend

$ echo $varl

Hello

$echo $var2

friend

$ echo $var3

$

Предположим, что пользователю программы unload в листинге 9.3 необходимо предоставить возможность прервать ее выполнение. Можно было бы вставить следующие строки кода:

echo "The following files will be unloaded"

ls -x $1

echo "Do you want to continue: Y or N c"

read ANSWER

if [ $ANSHER = N -o $ANSWER = n ]

then

exit 0

fi

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


Оператор case


Ранее в этом разделе было показано, что оболочка Bourne обеспечивает специальную конструкцию для часто встречающегося случая, когда вместо вложенных конструкций if-then-else можно использовать оператор elif. Еще один довольно часто встречающийся случай - ряд операторов elif, в которых одна и та же переменная проверяется на соответствие нескольким возможным условиям, как показано в следующем примере:

if [ variablel = valuel ]

then

command

command

elif [ variablel = value2 ]

then

command

command

elif [variablel = value3 ] then

command

command

fi

Оболочка Bourne обеспечивает более понятный и более мощный метод решения этой проблемы с помощью оператора case. Этот оператор более понятен, т.к. обходится без множества операторов elif и then. У него больше возможностей, поскольку он допускает использование шаблонов, во многом подобное интерпретатору командной строки. Оператор case допускает, чтобы значение было именованным, что почти всегда означает применение переменной, и допускает применение к значению символов шаблонов. Общий синтаксис оператора case следующий:

case value in

pattern1)

command

command;;

pattern2)

command

command;;

patternn)

command;

esac

Оператор case выполняет только один набор команд. Если значение соответствует нескольким шаблонам, выполняется только первый из указанных наборов команд. Двойная точка с запятой (;;) после команды действует в качестве ограничителя команд, подлежащих выполнению при соответствии значения Value конкретному условию.

В программе листинга 9.5 оператор case объединяет три примера программ - backup, restore и unload - в единую интерактивную программу, предоставляя возможность пользователю выбрать функцию из меню.


Листинг 9.5. Интерактивная программа архивирования.


# Интерактивная программа резервного копирования, восстановления

# или выгрузки каталога

echo "Welcome to the menu driven Archive program"

echo _

# Считывание и подтверждение имени каталога

echo "What directory do you want? c"

read WORKDIR

if [ ! -d $WORKDIR ]

then

echo "Sorry, $WORKDIR is not a directory"

exit 1

fi

# Превращение каталога в текущий рабочий каталог

cd $WORKDIR

# Отображение меню

echo "Make a Choice from the Menu below"

echo _

echo "1 Restore Archive to $WORKDIR"

echo "2 Backup $WORKDIR "

echo "3 Unload $WORKDIR"

echo

# Считывание и выполнение выбора пользователя

echo "Enter Choice: c"

read CHOICE

case "$CHOICE" in


echo "Restoring..."
cpio -i </dev/rmt0;;


echo "Archiving..."
ls | cpio -o >/dev/rmt0;;

3) echo "Unloading..."

ls | cpio -o >/dev/rmt0;;

*) echo "Sorry, $CHOICE is not a valid choice"

exit 1

esac

# Проверка ошибок cpio

if [ $? -ne 0 ]

then

echo "A problem has occurred during the process"

if [ $CHOICE - 3 ]

then

echo "The directory will not be erased"

fi

echo "Please check the device and try again"

exit 2

else

if [ $CHOICE = 3 ]

then

rm *

fi

fi

Обратите внимание на использование звездочки (*) в программе листинга 9.5 для определения действия по умолчанию в случае отсутствия соответствия со всеми остальными шаблонами в операторе case. Также обратите внимание, что проверка ошибок в процессе архивирования выполняется в программе только один раз. Такая проверка может быть выполнена в этой программе потому, что состояние выхода оператора case всегда является состоянием выхода последней выполненной команды. Поскольку все три случая заканчиваются выполнением команды cpio, а случай по умолчанию заканчивается оператором exit, переменная состояния выхода в этой программе всегда является состоянием выхода команды cpio.

Еще одна мощная возможность оператора case - возможность связывания нескольких шаблонов с одним и тем же набором команд. Для этого используется вертикальная черта (|) в качестве символа ог (или) в следующей форме:

patterni | pattern2 ) command

command;;

Изменив оператор case, можно еще больше модифицировать интерактивную программу архивирования, чтобы предоставить пользователю возможность сделать выбор, вводя номер пункта меню или первую букву названия функции:

read CHOICE

case "$CHOICE" in

1 | R ) echo "Restoring..."

cpio -i </dev/rmt0;;

2 | В ) echo "Archiving..."

ls | cpio -o >/dev/rmt0;;

3 | U ) echo "Unloading..."

ls | cpio -o >/dev/rmt0;;

*) echo "Sorry, $CHOICE is not a valid choice"

exit 1

esac

Обновлено: 12.03.2015