ГЛАВА 5 ПОСОБИЕ ПО SHELL 5.3 ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ SHELL Shell можно использовать для создания программ, являющихся, по сути дела, новыми командами. Такие программы еще называют shell-процедурами. В этом разделе описывается, как создавать и выполнять shell-программы, которые могут использовать команды, переменные, позиционные параметры, коды возврата и базовые уп- равляющие структуры. Примеры с shell-программами демонстрируются в этом разделе дву- мя способами. Во-первых, с помощью команды cat выводится на эк- ран содержимое файла, представляющего собой shell-программу: |$ cat тест_файл |первая команда | . | . | . |последняя команда |$ Во-вторых, результаты работы shell-программ появляются после выполнения командной строки: |$ тест_файл |вывод_программы |$ Прежде чем создавать shell-программы, необходимо освоить работу с текстовым редактором. 5.3.1 Shell-программы 5.3.1.1 Создание простой shell-программы Начнем с составления простой shell-программы, которая будет вы- полнять по порядку следующие действия: выводить имя текущего каталога выводить содержимое этого каталога отображать на экране терминала сообщение: "Конец shell- программы". Для этого создадим файл dl (сокращение от "directory list"). С с помощью одного из текстовых редакторов ввдем: |pwd |ls |echo Конец shell-программы. После этого закроем файл. На этом создание shell-программы за- вершается. Чтобы вывести содержимое файла на экран, воспользу- емся командой cat: |$ cat dl |pwd |ls |echo Конец shell-программы. |$ 5.3.1.2 Выполнение shell-программы Одним из способов выполнения shell-программы является использо- вание команды sh. Программа dl вызывается командой sh: |sh dl В результате работы программы сначала выводится маршрутное имя текущего каталога, затем список файлов текущего каталога и, на- конец, сообщение "Конец shell-программы". Команда sh предостав- ляет возможность проверить работоспособность shell-программы. Если команда dl оказалась полезной, ее можно объявить выполняе- мым файлом с помощью команды chmod; после этого, для запуска команды dl, достаточно ввести просто dl. В следующем примере показано использование команды chmod для объявления файла dl выполняемым, затем выполнена команда ls -l, чтобы проверить на- личие изменений в правах доступа к этому файлу. |$ chmod u+x dl |$ ls -l |total 2 |-rw------- 1 login login 3661 Nov 2 10:28 mbox |-rwx------ 1 login login 48 Nov 15 10:50 dl |$ Обратите внимание на то, что команда chmod добавляет для поль зователя (u) право на выполнение (+x). Теперь dl является ис- полняемой программой. Попытайтась выполнить ее, для чего введи- те: |dl Получается тот же результат, что и при вводе sh dl. Более под робные сведения о команде chmod можно найти в гл. 3. 5.3.1.3 Создание каталога bin для выполняемых файлов Для того, чтобы обеспечить доступ к своим shell-программам из любого каталога, пользователь может создать во входном каталоге каталог bin, а затем переместить в него свои shell-файлы. Кроме того, пользователь должен изменить shell-переменную PATH, включив в нее свой каталог bin: |PATH=$PATH:$HOME/bin Дополнительные сведения о переменной PATH содержатся в разделах "Переменные" и "Использование shell-переменных". Следующий пример дает возможность вспомнить, какие команды не обходимо выполнить для создания каталога с собственными выпол няемыми файлами. В данном случае предполагается, что файл dl находится во входном каталоге: |cd |mkdir bin |mv dl bin/dl Чтобы проверить, получил ли файл dl разрешение на выполнение, следует перейти в каталог bin и выполнить команду ls -l. Теперь перейдите в какой-либо каталог, отличный от входного, и попробуйте выполнить команду dl. Каков результат? В таблице приводится краткое описание новой shell-программы dl. | +-----------------------------------------------------------------------+ | | Краткое описание shell-программы | | | | | | dl - вывести маршрутное имя каталога и его содержимое | | | (каталог определяется пользователем) | | +-----------------------------------------------------------------------| | | команда аргументы | | +-----------------------------------------------------------------------| | | dl отсутствуют | | +-----------------------------------------------------------------------| | | Описание: Команда dl отображает на экране вывод команд pwd | | | и ls. | | +-----------------------------------------------------------------------+ Каталогу bin можно присвоить и другое имя. В этом случае при- дется снова изменить значение переменной PATH. 5.3.1.4 Предостережение об именовании shell-программ Shell-программе можно присвоить любое имя. Но ей не следует да- вать такое же имя, как у какой-либо команды системы. Если сде- лать это, система вместо своей команды будет выполнять команду пользователя. Например, если shell-программе dl присвоить имя mv, при каждой попытке переместить файл, система будет выпол- нять вместо команды mv shell-программу из каталога пользовате- ля. Другая проблема возникает, если файлу dl дать имя ls и попы- таться выполнить этот файл. При этом образуется бесконечный цикл, поскольку shell-программа выполняет команду ls. Через не- которое время система выдаст следующее сообщение об ошибке: |Too many processes, cannot fork (слишком много процессов, не могу создать новый процесс). Что произошло? Запущена новая команда ls. Shell ввел и выполнил команду pwd. Затем он прочитал команду ls из shell-программы и попытался выполнить опять shell-программу с именем ls. Так воз- ник бесконечный цикл. Разработчики системы UNIX поступили мудро, установив предел числу повторений бесконечного цикла. Одним из способов оградить себя от подобных неприятностей является указание в собственной shell-программе для системной команды ls маршрутного имени /bin/ls. Следующая shell-программа с именем ls уже работала бы правиль но: |$ cat ls |pwd |/bin/ls |echo Конец shell-программы |$ Если пользователь назвал свою shell-программу именем ls, сис- темную команду ls можно выполнить только путем указания ее пол- ного маршрутного имени. 5.3.2 Переменные Переменные являются основными объектами данных для shell-прог- рамм, с которыми они имеют дело. В этом разделе обсуждаются три типа переменных и способы их использования: позиционные параметры специальные параметры именованные переменные 5.3.2.1 Позиционные параметры Позиционным параметром служит переменная shell-программы, зна- чение которой устанавливается в соответствии с указанным в ко- мандной строке аргументом. Позиционные параметры нумеруются, и на них можно ссылаться по номеру с предшествующим знаком $: $1, $2, $3 и т.д. Shell-программа может обращаться к девяти позиционным парамет- рам. Если она вызывается с помощью командной строки типа следу- ющей: |shell.prog pp1 pp2 pp3 pp4 pp5 pp6 pp7 pp8 pp9 то позиционный параметр $1 в shell-программе получит значение pp1, позиционный параметр $2 - значение pp2 и т.д. Для приобретения навыков в подстановке значений позиционных па- раметров создайте файл с именем pp (сокращение от "positional parameters"). Затем включите в файл команды echo так, чтобы за- пуск команды cat с указанием файла pp давал бы на экране следу- ющее: |$ cat pp |echo Первый позиционный параметр: $1 |echo Второй позиционный параметр: $2 |echo Третий позиционный параметр: $3 |echo Четвертый позиционный параметр: $4 |$ Если выполнить эту shell-программу с аргументами one, two, three и four, на экране будет получен следующий результат (пе- ред этим необходимо с помощью команды chmod объявить shell программу pp выполняемой): |$ chmod u+x pp |$ pp one two three four |Первый позиционный параметр: one |Второй позиционный параметр: two |Третий позиционный параметр: three |Четвертый позиционный параметр: four |$ Ниже показана shell-программа с именем bbday, которая посылает поздравление пользователю с входным именем, вводимым в команд ной строке: at bbday |banner happy birthday | mail $1 |$ Попробуйте послать поздравление с днем рождения самому себе. Если у Вас входное имя sue, командная строка будет иметь вид: |bbday sue В таблице приводится краткое описание синтаксиса и возможностей shell-программы bbday. | +-----------------------------------------------------------------------+ | | Краткое описание shell-программы | | | | | | bbday - послать поздравление с днем рождения плакатными | | | буквами (адресат определяется пользователем) | | +-----------------------------------------------------------------------| | | команда аргументы | | +-----------------------------------------------------------------------| | | bbday входное_имя | | +-----------------------------------------------------------------------| | | Описание: Команда bbday посылает поздравление с днем рождения | | | плакатными буквами пользователю с указанным входным | | | именем. | | +-----------------------------------------------------------------------+ Команда who выводит список всех пользователей, работающих в те- кущий момент в системе. Как создать небольшую shell-программу с именем whoson, которая будет указывать, работает ли в настоящее время в системе пользователь с определенным входным именем? Введите в файл whoson следующую командную строку: |who | grep $1 Команда who выдает список всех текущих пользователей системы, а команда grep выполняет поиск в выходной строке команды who, со- держащей цепочку символов, которая включает в себя значение по- зиционного параметра $1. Попытайтесь указать свое входное имя в качестве аргумента в вы- зове новой shell-программы whoson. Пусть для определенности это будет имя sue. После ввода команды whoson параметр $1 в shell- программе заменяется на sue, и она выполняется в следующем ви- де: |who | grep sue На экране будет следующий результат: |$ whoson sue |sue tty26 Jan 24 13:35 |$ Если пользователь с указанным входным именем не работает в те- кущий момент в системе, то команда grep не завершается успешно и whoson ничего не выводит. В таблице приводится краткое описание синтаксиса и возможностей команды whoson. | +-----------------------------------------------------------------------+ | | Краткое описание shell-программы | | | | | | whoson - вывести информацию о том, работает ли | | | указанный пользователь в системе (входное | | | имя определяется пользователем) | | +-----------------------------------------------------------------------| | | команда аргументы | | +-----------------------------------------------------------------------| | | whoson входное_имя | | +-----------------------------------------------------------------------| | | Описание: Если указанный пользователь работает в системе, ко- | | | манда whoson выводит его входное имя, номер терминала | | | и время/дату входа в систему. | | +-----------------------------------------------------------------------+ Shell допускает в командной строке до 128 аргументов. Однако shell-программа в каждый момент времени может иметь доступ только к девяти параметрам от $1 до $9. Это ограничение мож преодолеть с помощью команды shift. Описанный в следующем раз- деле параметр $* тоже может быть использован для доступа ко всем аргументам командной строки. 5.3.2.2 Специальные параметры $# Этот параметр при обращении к нему в shell-прог- рамме содержит число аргументов в вызове данной shell-программы. Включите показанную ниже командную строку в выполняемую shell- программу с именем get.num, а затем выполните команду cat get.- num: |$ cat get.num |echo Число аргументов: $# |$ Эта shell-программа просто выводит число аргументов, которое указано при ее вызове. Например: |$ get.num test out this program |Число аргументов: 4 |$ В таблице приводится краткое описание shell-программы get.num. | +-----------------------------------------------------------------------+ | | Краткое описание shell-программы | | | | | | get.num - подсчитать и вывести число аргументов, | | | заданных пользователем | | +-----------------------------------------------------------------------| | | команда аргументы | | +-----------------------------------------------------------------------| | | get.num строка_символов | | +-----------------------------------------------------------------------| | | Описание: Команда get.num подсчитывает число аргументов, | | | указанное в вызове команды, и затем выводит его. | | | | | | Примечание: Эта команда демонстрирует использование специального | | | параметра $#. | | +-----------------------------------------------------------------------+ $* Этот параметр при обращении к нему в shell-прог- рамме содержит строку, включающую в себя все аргу- менты, которые указаны в вызове этой shell-прог- раммы, начиная с первого. Здесь нет ограничения только до девяти параметров, как в случае позици- онных параметров от $1 до $9. Для демонстрации действия специального символа $* можно напи- сать простую программу, которая выведет все параметры, указан- ные в ее вызове. Назовем ее show.param: |$ cat show.param |echo Параметры этой команды: $* |$ Shell-программа show.param отобразит все аргументы, содержащие- ся в ее вызове. Объявите файл show.param выполняемым и выполни- те его, используя следующие аргументы: |Hello. How are you? (Здравствуйте. Как поживаете?) |$ show.param Hello. How are you? |Параметры этой команды: Hello. How are you? |$ Заметьте, что shell-программа show.param выводит Hello. How are you? А теперь попытайтесь выполнить эту shell-программу, ис пользуя более девяти параметров: |$ show.param one two 3 4 5 six 7 8 9 10 11 |Параметры этой команды: one two 3 4 5 six 7 8 9 10 11 |$ И здесь shell-программа show.param выводит все заданные аргу- менты. Параметр $* может быть полезен в тех случаях, когда при указании аргументов в вызове shell-программы используется рас- ширение имени файла. Применим возможность расширения имени файла в команде show.pa- ram. Допустим, в каталоге есть несколько файлов, имена которых соответствуют главам книги: chap1, chap2 и т.д. до chap7. Выве дем список этих файлов: |$ show.param chap? |Параметры этой команды: chap1 chap2 chap3 |chap4 chap5 chap6 chap7 |$ В таблице приводится краткое описание shell-программы show.pa- ram. | +-----------------------------------------------------------------------+ | | Краткое описание shell-программы | | | | | | show.param - вывести все позиционные параметры | | | (задаются пользователем) | | +-----------------------------------------------------------------------| | | команда аргументы | | +-----------------------------------------------------------------------| | | show.param (любые позиционные параметры) | | +-----------------------------------------------------------------------| | | Описание: Команда show.param выводит все параметры. | | | | | | Примечание: Если параметрами являются сопоставляемые имена | | | файлов (с помощью метасимвола), команда будет | | | выводить каждое имя файла. | | +-----------------------------------------------------------------------+ 5.3.2.3 Именованные переменные Другой формой переменной, используемой в shell-программах, яв- ляется именованная переменная. Значения именованной переменной присваиваются самим пользователем, и эта операция имеет следую- щий формат: |именованная_переменная=значение Обратите внимание на то, что по обе стороны от знака = отсутст- вуют пробелы. В приведенном ниже примере, именованной переменной var1 прис- ваивается значение myname, являющееся числовой величиной или цепочкой символов: |var1=myname Для обращения в shell-программе к значению переменной, перед ее именем следует указать знак $. С учетом приведенного выше при- мера, ссылка $var1 говорит shell о том, что везде, вместо це- почки символов $var1, необходимо подставить значение myname, присвоенное переменной var1. Первым символом имени переменной должна быть буква или знак подчеркивания. Остальная часть имени может быть совокупностью букв, знаков подчеркивания и цифр. Как и в случае имен shell пррограмм, не следует, в качестве имени переменной, использо- вать имена команд shell. Кроме того, некоторые имена переменных зарезервированы интерпретатором shell, и их нельзя применять в качестве имен переменных пользователя. Ниже дается краткое опи- сание этих зарезервированных переменных shell: CDPATH определяет маршрут поиска для команды cd. HOME переменная для команды cd, задаваемая по умолча- нию (основной каталог). IFS определяет разделители внутренних полей (обычно пробел, табуляция и возврат каретки). LOGNAME логическое имя пользователя. MAIL устанавливает имя файла, который содержит элект ронную почту пользователя. PATH определяет маршрут поиска команд для shell. PS1 определяет первичное приглашениe (по умолчанию $). PS2 определяет вторичное приглашение (по умолчанию >). TERM идентифицирует тип терминала пользователя. Эта переменная имеет большое значение при работе с интерактивными программными системами. TERMINFO указывает на каталог, в котором можно найти ин- формацию о терминале пользователя. TZ определяет поясное время (по умолчанию MDT-3). Многие из этих переменных рассматриваются ниже, в данной главе, в разделе "Модификация входного окружения пользователя". Более подробно об этих переменных можно прочитать в разделе sh(1) Справочника пользователя. Значения этих переменных можно проверить двумя способами. Во- первых, вводя команду: |echo $имя_переменной Система выводит значение переменной имя_переменной. Во-вторых, можно воспользоваться командой env(1), которая выводит значения всех определенных в shell'е переменных. Для этого, следует просто ввести env; в ответ система выведет список переменных и их значений. 5.3.2.4 Присваивание значения переменной Как известно, переменную TERM можно установить, вводя командную строку: |TERM=имя_переменной Это простейший способ присваивания значения переменной. Существуют и другие способы: использование команды read; переадресация вывода команды на переменную; присваивание переменной значения позиционного парамет- ра. Ниже все эти способы подробно рассматриваются. 5.3.2.4.1 Использование команды read Используемая в shell-программе команда read позволяет напоми- нать пользователю о необходимости задания значений переменных. Общий формат команды read: |read переменная Присвоенные командой read значения переменной с именем перемен- ная будут подставлены во всей программе вместо $переменная. Ес- ли в программе перед командой read вставить команду echo, можно выводить пользователю подсказку (типа "Введите ...") о необхо- димости ввода значения переменной. Команда read будет ждать до тех пор, пока пользователь не введет строку символов; затем она установит эту строку в качестве значения переменной. В приведенном ниже примере показана простая shell-программа с именем num.please для поиска в списке телефонов по имени. В этой программе используются следующие команды: echo для вывода подсказки на ввод последнего имени read для присваивания вводимого значения переменной с именем name grep для выполнения поиска в файле list по значению этой переменной Законченная программа могла бы иметь примерно такой вид: |$ cat num.please |echo Введите фамилию: |read name |grep $name list |$ Создайте файл с именем list, который содержит несколько фамилий и номеров телефонов, а затем запустите shell-программу num.ple- ase. Следующий пример - программа с именем mknum, которая создает список телефонов. Эта программа включает в себя такие команды: echo выводит подсказку на ввод фамилии read присваивает вводимую фамилию переменной name echo запрашивает номер телефона read присваивает вводимый номер телефона переменной num echo добавляет значения переменных name и num к файлу list Для того, чтобы вывод команды echo добавить к файлу list, этот вывод необходимо переадресовать с помощью символа >>. Если ис- пользовать символ >, то файл list будет содержать только пос- ледний номер телефона, который добавляется в список. Содержимое shell-программы mknum можно вывести, выполнив коман- ду cat. Программа, имеющая представленный ниже вид, уже может быть объявлена исполняемой (с помощью команды chmod): |$ cat mknum |echo Введите фамилию |read name |echo Введите номер |read num |echo $name $num >> list |$ chmod u+x mknum |$ Теперь попытайтесь выполнить эти новые программы, работающие со списком телефонов. В показанном ниже примере, программа mknum сначала создает новый элемент списка телефонов для Mr. Niceguy, а затем программа num.please выводит его номер: |$ mknum |Введите фамилию |Mr. Niceguy |Введите номер |668-0007 |$ num.please |Введите фамилию |Niceguy |Mr. Niceguy 668-0007 |$ Заметим, что переменная name допускает в качестве значения как Mr., так и Niceguy. В последующих таблицах приводится краткое описание shell-прог- рамм mknum и num.please. | +-----------------------------------------------------------------------+ | | Краткое описание shell-программы | | | | | | mknum - поместить фамилию и номер телефона в список | | +-----------------------------------------------------------------------| | | команда аргументы | | +-----------------------------------------------------------------------| | | mknum (вводятся в интерактивном режиме) | | +-----------------------------------------------------------------------| | | Описание: Запрашивает фамилию человека и номер его телефона, и | | | добавляет их в список телефонов пользователя. | | | | | | Примечание: Это интерактивная команда. | | +-----------------------------------------------------------------------+ | +-----------------------------------------------------------------------+ | | Краткое описание shell-программы | | | | | | num.please - вывести фамилию человека и номер его | | | телефона | | +-----------------------------------------------------------------------| | | команда аргументы | | +-----------------------------------------------------------------------| | | num.please (вводятся в интерактивном режиме) | | +----------------------------------------------------------------------- | | Описание: Запрашивает фамилию человека и затем выводит его | | полное имя и номер телефона. | | | | Примечание: Это интерактивная команда. ---------------+ 5.3.2.4.2 Подстановка вывода команды в качестве значения переменной Вывод команды можно использовать в качестве значения перемен- ной, если применить подстановку результата выполнения команды. Формат подстановки: |переменная=`команда` Вывод команды, заключенной в обратные кавычки, становится зна- чением переменной с именем переменная. В одном из ранее приведенных примеров, с использованием кон- вейера, команда date объединялась с командой cut для вывода точного времени. Эта командная строка имела следующий вид: |date | cut -c12-19 Указанную командную строку можно включить в простую shell-прог- рамму с именем t, которая будет выводить время: |$ cat t |time=`date | cut -c12-19` |echo Текущее время: $time |$ Запомните, что по обе стороны от знака равенства не должно быть пробелов. Объявив файл t исполняемым, получим программу, кото рая выводит только время: |$ chmod u+x t |$ t |Текущее время: 10:36 |$ В таблице приводится краткое описание shell-программы t. | +-----------------------------------------------------------------------+ | | Краткое описание shell-программы | | | | | | t - вывести текущее время | | +-----------------------------------------------------------------------| | | команда аргументы | | +-----------------------------------------------------------------------| | | t отсутствуют | | +-----------------------------------------------------------------------| | | Описание: Команда t выводит точное время в часах и минутах. | | +-----------------------------------------------------------------------+ 5.3.2.4.3 Присваивание значений с помощью позиционных параметров Именованной переменной можно присвоить значение позиционного параметра, если использовать следующий формат оператора присва- ивания: |var1=$1 В приведенной ниже простой shell-программе с именем simp.p, именованной переменной присваивается значение позиционного па- раметра: |$ cat simp.p |var1=$1 |echo $var1 |$ Переменной можно присваивать и вывод команды, которая использу- ет позиционные параметры, например: |person=`who | grep $1` В показанном ниже примере, программа log.time сохраняет резуль- таты работы программы whoson (была описана выше). Вывод команды whoson присваивается переменной person и добавляется с помощью команды echo к файлу login.file. Наконец, последняя команда ec- ho выводит значение $person, которое совпадает с выводом коман- ды whoson: |$ cat log.time |person=`who | grep $1` |echo $person >> login.file |echo $person |$ Ответ системы по выполнении команды log.time будет иметь при- мерно такой вид: |$ log.time maryann |maryann tty61 Apr 11 10:26 |$ В таблице приводится краткое описание shell-программы log.time. | +-----------------------------------------------------------------------+ | | Краткое описание shell-программы | | | | | | log.time- внести в список и вывести на экран данные о | | | пользователе с указанным входным именем | | +-----------------------------------------------------------------------| | | команда аргументы | | +-----------------------------------------------------------------------| | | log.time входное_имя | | +-----------------------------------------------------------------------| | | Описание: Если пользователь, с указанным входным именем, | | | находится в текущий момент в системе, команда | | | log.time помещает строку данных об этом пользо- | | | вателе, полученную от команды who, в файл с именем | | | login.file и затем выводит эту строку на терминал. | | +-----------------------------------------------------------------------+ 5.3.3 Конструкции языка программирования shell Язык программирования shell имеет несколько конструкций, кото- рые придают дополнительную гибкость программам пользователя: Комментарии - позволяют описывать действия программы. Подстановка строк в команды - позволяет включать в shell-программу строки, которые в результате переадресации должны слу- жить вводом для некоторой команды этой же shell-программы. Команда exit - позволяет завершать shell-программу в точке, которая не является концом программы, и исполь- зовать коды возврата. Конструкции for и while - позволяют циклически повторять группу команд. Условные конструкции if и case - выполняют группу команд только в случае удов- летворения определенных условий. Команда break - позволяет выполнять безусловный выход из цикла. 5.3.3.1 Комментарии Комментарии в shell-программу могут быть включены двумя спосо- бами. Интерпретатор shell игнорирует весь текст в строке, кото- рый следует за знаком #. Знак # может быть указан в начале строки (в этом случае комментарий занимает целую строку) или после команды. В последнем случае команда нормально выполняет ся, но оставшаяся часть строки игнорируется интерпетатором shell. Конец строки является и концом комментария. Общий формат строки комментария: |#комментарий Например, программа, которая содержит следующие строки, при своем выполнении будет их игнорировать. |# Эта программа посылает поздравление с днем рождения. |# Этой программе необходимо входное имя пользователя |# в качестве аргумента. Комментарии удобны для описания действий программы и могут быть включены в любую программу. 5.3.3.2 Подстановка строк в команду В shell-программу можно включать строки, которые в результате переадресации должны служить вводом для некоторой команды этой же shell-программы. Этот способ обеспечивает ввод в команду shell-программы без использования отдельного файла. Нотация та кой подстановки состоит из символа переадресации << и ограничи- теля, который определяет начало и конец вводимых строк. Ограни- чителем может быть отдельный символ или цепочка символов; часто в качестве него используется восклицательный знак !. Общий формат подстановки строк в команду: | +---------------------------------------+ | | команда << ограничитель | | | ...входные строки... | | | ограничитель | | +---------------------------------------+ Программа gbday, показанная ниже в примере, использует подста- новку строк в команду для посылки поздравления с днем рождения путем переадресации строки ввода на команду mail: |$ cat gbday |mail $1 << ! |Наилучшие пожелания по случаю дня рождения. |! |$ При вызове этой команды необходимо задать входное имя получате ля в качестве аргумента команды. Вводом, который выполняется при использовании подстановки строк в команду, служит строка: |Наилучшие пожелания по случаю дня рождения. Например, для передачи этого поздравления пользователю с вход- ным именем mary следует ввести: |$ gbday mary Этот пользователь получит поздравление при чтении своей почты. |$ mail |From mylogin Wed May 14 14:31 CDT 1989 |Наилучшие пожелания по случаю дня рождения. |$ В таблице приводится краткое описание формата и возможностей команды gbday. | +-----------------------------------------------------------------------+ | | Краткое описание shell-программы | | | | | | gbday - послать поздравление с днем рождения | | | (получатель определяется пользователем) | | +-----------------------------------------------------------------------| | | команда аргументы | | +-----------------------------------------------------------------------| | | gbday входное_имя | | +-----------------------------------------------------------------------| | | Описание: Команда gbday посылает поздравление с днем рождения | | | пользователю с входным именем, указанным в качестве | | | аргумента команды. | | +-----------------------------------------------------------------------+ 5.3.3.3 Коды возврата Большинство команд shell выдает коды возврата, которые указыва- ют на то, успешно ли завершилось выполнение команды. В соот- ветствии с принятым соглашением, если возвращаемое значение равно 0 (нуль), команда завершилась успешно; если другое значе- ние, то неуспешно. Код возврата автоматически не выводится, но он доступен, как значение специального параметра shell'а - $?. 5.3.3.3.1 Проверка кодов возврата После выполнения команды в интерактивном режиме, ее код возвра- та можно вывести с помощью команды: |echo $? Рассмотрим следующий пример: |$ cat hi |Это файл hi. |$ echo $? |0 |$ cat hello |cat: cannot open hello |$ echo $? |2 |$ В первом случае файл hi существует в текущем каталоге и пользо ватель имеет право его читать. Команда cat выполняется так, как ожидается, и выводит содержимое файла. Она завершается с кодом возврата 0, который в примере показан как значение параметра $?. Во втором случае файл либо не существует, либо пользователь не имеет права на его чтение. Команда cat выводит диагностичес- кое сообщение и завершается с кодом возврата 2. 5.3.3.3.2 Использование кодов возврата с командой exit Shell-программа обычно прекращается после выполнения последней команды файла. Однако для прекращения выполнения программы в произвольной точке можно воспользоваться командой exit. Но, возможно, более важно то, что команду exit можно применять для выдачи кодов возврата. Более подробная информация о команде exit содержится в разделе exit(2) Справочника пользователя. 5.3.3.4 Циклы В рассмотренных выше примерах, команды в shell-программах вы- полнялись последовательно. Конструкции для организации циклов for и while позволяют выполнять в программе одну команду или их последовательность несколько раз. 5.3.3.4.1 Цикл for Цикл for выполняет последовательность команд для каждого эле мента из заданного списка. Он имеет следующий формат: | +---------------------------------------+ | | for переменная | | | in список_значений | | | do | | | команда1 | | | команда2 | | | . | | | . | | | . | | | последняя команда | | | done | | +---------------------------------------+ При каждом повторении цикла, переменной, заданной в заголовке цикла for, присваивается значение следующего элемента из спис- ка. Обращения к этой переменной могут присутствовать в любой команде тела цикла, находящегося между do и done. Shell-программу легче воспринимать, если конструкции цикла представлены наглядно. Поскольку shell игнорирует пробелы в на- чале строк, каждая секция команд может быть записана с отсту- пом, как это показано в приведенном выше формате. В то же вр мя, благодаря этим отступам, легко можно проверить наличие для каждого do, соответствующего done, в конце цикла. Переменная цикла может иметь призвольное имя. Например, если назвать эту переменную именем var, то var поочередно будут присваиваться значения из списка, задаваемого после ключевого слова in. Обращения к значению переменной в командах цикла за- даются в виде $var. Если ключевое слово in опущено, значениями переменной var будет весь набор аргументов, указанный в вызове команды и доступный через специальный параметр $*. Список ко- манд между ключевыми словами do и done будет выполняться для каждого значения переменной. Когда команды тела цикла выполнены для последнего значения в списке значений переменной, программа перейдет к выполнению следующей за done строки. Если за done строки отсутствуют, программа завершается. Конструкции языка программирования shell лучше всего разбирать на примере. Создадим программу, которая будет перемещать файлы из одного каталога в другой, используя: echo для выдачи пользователю приглашения о необходи мости ввода маршрутного имени для нового ката лога read для присваивания маршрутного имени переменной path for переменная для организации цикла с переменной file в к честве переменной цикла; в командах, которые входят в тело цикла, к значению этой переменной следует обращаться посредством $file in список_значений для указания списка значений; если ключевое слово in отсутствует, списком значений служит $* (все аргументы, введенные в командной стро- ке) do последовательность_команд для задания последовательности команд; в данной программе эта конструкция будет иметь вид: |do | mv $file $path/$file |done Ниже показан текст shell-программы mv.file: |$ cat mv.file |echo Введите маршрутное имя каталога |read path |for file | in memo1 memo2 memo3 |do | mv $file $path/$file |done |$ В данной shell-программе значения для переменной file находятся в самой программе. Чтобы можно было менять имена файлов при каждом вызове программы, значения переменным следует присваи- вать, используя позиционные параметры или команду read. Когда используются позиционные параметры, в ключевом слове in нет не- обходимости. Это показано в следующем примере: |$ cat mv.file |echo Введите маршрутное имя файла |read path |for file |do | mv $file $path/$file |done |$ С помощью этой команды можно переместить из каталога в каталог сразу несколько файлов, если при вызове команды в качестве ее аргументов указать список имен требуемых файлов. (Легче всего это сделать, используя описанный ранее механизм расширения име- ни файла.) В таблице дается краткое описание shell-программы mv.file. | +-----------------------------------------------------------------------+ | | Краткое описание shell-программы | | | | | | mv.file - переместить файлы в другой каталог | | | (указывается пользователем) | | +-----------------------------------------------------------------------| | | команда аргументы | | +-----------------------------------------------------------------------| | | mv.file имена_файлов | | | (вводятся в интерактивном | | | режиме) | | +-----------------------------------------------------------------------| | | Описание: Перемещает файлы в новый каталог. | | | | | | Примечания: Эта программа требует задания имен файлов как | | | аргументов. Программа выводит подсказку о необхо- | | | димости ввода маршрутного имени нового каталога. | | +-----------------------------------------------------------------------+ 5.3.3.4.2 Цикл while Другая конструкция - цикл while, состоит из двух групп команд. В этой конструкции последовательность команд второй группы, на- ходящаяся между do и done, будет выполняться до тех пор, пока группа команд в списке while после своего завершения возвращает код состояния true (означающий, что команды второй группы могут быть выполнены). Общий формат цикла while: | +---------------------------------------+ | | while | | | команда1 | | | . | | | . | | | . | | | последняя команда | | | do | | | команда1 | | | . | | | . | | | . | | | последняя команда | | | done | | +---------------------------------------+ Показанная ниже программа с именем enter.name использует цикл while для ввода в файл списка фамилий. |$ cat enter.name |while | read x |do | echo $x >> xfile |done |$ С некоторыми полезными дополнениями программа приобретает такой вид: |$ cat enter.name |echo Вводите фамилии по одной в строке (используйте ) |echo Список фамилий завершите вводом <^d> |while read x |do | echo $x >> xfile |done |echo xfile содержит следующие фамилии: |cat xfile |$ Обратите внимание на то, что после завершения цикла выполняются команды, находящиеся за done. Если в первых двух командах echo используются специальные сим- волы, для отмены их специального смысла необходимо применять кавычки. Ниже показан пример результата работы программы en- ter.name: |$ enter.name |Вводите фамилии по одной в строке (используйте ) |Список фамилий завершите вводом <^d> |Mary Lou |Janice |<^d> |xfile содержит следующие фамилии: |Mary Lou |Janice |$ Заметим, что после завершения цикла программа выводит все име- на, содержащиеся в xfile. 5.3.3.5 "Черная дыра" shell'а: /dev/null В файловой системе UNIX имеется файл с именем /dev/null, в ко- торый через shell можно передавать весь нежелательный вывод. Попробуйте изменить нормальный результат работы команды who, используя файл /dev/null. Сначала введите команду who обычным образом. Ответом системы является список работающих в системе пользователей. После этого введите команду who, но переадресуй- те ее вывод на /dev/null: |who > /dev/null Обратите внимание на то, что система вывела только приглашение. Вывод команды who был передан в файл /dev/null, а по существу был отброшен. 5.3.3.6 Условные конструкции 5.3.3.6.1 Конструкция if...then Последовательность команд, находящаяся за then, выполняется только тогда, когда результат выполнения помещенного после if списка команд оказывается успешным. Оператор if заканчивается ключевым словом fi. Общий формат конструкции if...then: | +-----------------------------------------------+ | | if | | | команда1 | | | . | | | . | | | . | | | последняя команда | | | then | | | команда1 | | | . | | | . | | | . | | | последняя команда | | | fi | | +-----------------------------------------------+ В качестве примера рассмотрим shell-программу с именем search, которая демонстрирует использование конструкции if...then. В этой программе для поиска слова в файле применяется команда grep. Если эта команда завершается успешно, shell-программа ко- мандой echo выведет сообщение о том, что слово в файле найдено. Ниже показан текст shell-программы search: |$ cat search |echo Введите слово и имя файла. |read word file |if grep $word $file | then echo Слово $word содержится в файле $file |fi |$ Обратите внимание на то, что команда read присваивает значения двум переменным. Совокупность первых символов, вплоть до пробе- ла, присваивается в качестве значения переменной word. Осталь- ные символы, включая имеющиеся пробелы, присваиваются перемен- ной file. При работе этой программы возникает проблема, связанная с неже- лательным выводом команды grep. Если необходимо избавиться от этого вывода, следует использовать файл /dev/null, изменив ко- мандную строку if следующим образом: |if grep $word $file > /dev/null Если теперь выполнить shell-программу search, то должен быть получен ответ только в виде сообщения, указанного в echo. 5.3.3.6.2 Конструкция if...then...else Конструкция if...then может содержать и альтернативный набор команд (после ключевого слова else), который выполняется в том случае, когда результатом завершения последовательности команд в if является значение false. Такой оператор имеет следующий формат: | +-----------------------------------------------+ | | if | | | команда1 | | | . | | | . | | | . | | | последняя команда | | | then | | | команда1 | | | . | | | . | | | . | | | последняя команда | | | else | | | команда1 | | | . | | | . | | | . | | | последняя команда | | | fi | | +-----------------------------------------------+ Теперь можно улучшить shell-программу search таким образом, чтобы она выводила сообщение пользователю и о том, что не может найти заданное слово в файле. Улучшенный вариант программы при- мет вид: |$ cat search |echo Введите слово и имя файла. |read word file |if | grep $word $file > /dev/null |then | echo Слово $word содержится в файле $file |else | echo Слово $word ОТСУТСТВУЕТ в файле $file |fi |$ В таблице приводится краткое описание улучшенной версии shell- программы search. | +---------------------------------------------------------------------+ | | Краткое описание shell-программы | | | | | | search - определить, содержится ли слово в файле | | | (слово и файл определяются пользователем) | | +---------------------------------------------------------------------| | | команда аргументы | | +---------------------------------------------------------------------| | | search вводятся в интерактивном | | +---------------------------------------------------------------------| | | Описание: Команда сообщает о том, содержится ли слово в | | | файле. | | | | | | Примечание: Команда выводит подсказку о необходимости ввода | | | аргументов (слова и имени файла). | | +---------------------------------------------------------------------+ 5.3.3.6.3 Команда test Команду test удобно применять в условных конструкциях. Она про- веряет истинность некоторых условий. Если условие истинно (true), то действия в ветви then будут продолжены, а если оно ложно (false), будет выполняться следующая команда. Ниже предс- тавлены некоторые полезные опции команды test: |test -r файл true, если файл существует и его можно читать |test -w файл true, если файл существует и есть разрешение на запись в него |test -x файл true, если файл существует и являет- ся выполняемым |test -s файл true, если файл существует и содер- жит хотя бы один символ |test var1 -eq var2 true, если var1 равно var2 |test var1 -ne var2 true, если var1 не равно var2 В процессе работы в системе UNIX у пользователя может возник- нуть потребность в создании shell-программы, которая перемещает все выполняемые файлы текущего каталога в его каталог bin. Для выбора выполняемых файлов можно использовать команду test -x. Рассмотрим пример с оператором for, который встречался в прог рамме mv.file: |$ cat mv.file |echo Введите маршрутное имя файла. |read path |for file |do | mv $file $path/$file |done |$ Создадим программу с именем mv.ex, которая включает в цикл do...done оператор if test -x для перемещения только выполняе- мых файлов. Эта программа будет иметь следующий вид: |$ cat mv.ex |echo Введите маршрутное имя файла. |read path |for file | do | if test -x $file | then | mv $file $path/$file | fi | done |$ Маршрутом каталога будет путь из текущего каталога в каталог bin. Однако если использовать значение переменной HOME, то от- падает необходимость каждый раз вводить маршрутное имя катало- га. Значение $HOME определяет входной каталог, а $HOME/bin - маршрут к каталогу bin пользователя. В показанном ниже примере, программа mv.ex не выводит подсказку о необходимости ввода имени каталога и в ней отсутствует даже ввод переменной path: |$ cat mv.ex |for file | do | if test -x $file | then | mv $file $HOME/bin/$file | fi | done |$ Проверим работу программы для всех файлов текущего каталога, задав их с помощью метасимвола *, как аргумента команды. В сле- дующем примере сначала команда mv.ex выполняется из текущего каталога, а затем осуществляется переход в каталог bin и выво- дится список его файлов. В этом каталоге теперь должны быть все выполняемые файлы каталога, ранее являвшегося текущим. |$ mv.ex * |$ cd;cd bin;ls |список_выполняемых_файлов |$ В таблице приводится краткое описание формата и возможностей shell-программы mv.ex. | +---------------------------------------------------------------+ | | Краткое описание shell-программы | | | | | | mv.ex - переместить все выполняемые файлы текущего | | | каталога в каталог bin | | +---------------------------------------------------------------| | | команда аргументы | | +---------------------------------------------------------------| | | mv.ex * (имена всех файлов) | | +---------------------------------------------------------------| | | Описание: Перемещает все файлы текущего каталога, | | | имеющие разрешение на выполнение, в | | | каталог bin. | | | | | | Примечания: Все выполняемые файлы в каталоге bin (или | | | другом каталоге, указываемом переменной | | | PATH) могут быть запущены из любого ката- | | | лога. | | +---------------------------------------------------------------+ 5.3.3.6.4 Конструкция case...esac Конструкция case...esac обеспечивает возможность множественного выбора. Она позволяет задать несколько шаблонов для сопоставле- ния и, затем, выполнять только нужный набор команд. Секция вы- бора вариантов по шаблону должна начинаться с ключевого слова in, а за последним символом каждого шаблона должна быть помеще- на скобка ). Последовательность команд каждого шаблона заверша- ется символами ;;. Конструкция case должна заканчиваться ключе- вым словом esac (обратное от "case"). Общий формат конструкции case: | +-------------------------------------------+ | | case слово | | | in | | | шаблон1) | | | командная строка 1 | | | . | | | . | | | . | | | последняя командная строка | | | ;; | | | шаблон2) | | | командная строка 1 | | | . | | | . | | | . | | | последняя командная строка | | | ;; | | | шаблон3) | | | командная строка 1 | | | . | | | . | | | . | | | последняя командная строка | | | ;; | | | *) | | | команда 1 | | . | | . | | . | | последняя команда | | ;; | | esac | +-------------------------------------------+ Оператор case сопоставляет значение слово, которое следует за ключевым словом case, с цепочкой символов шаблон в первой сек- ции выбора варианта. Если они совпадают, в программе выполняют- ся командные строки, находящиеся между первым шаблоном и соот- ветствующим (;;) ограничителем. Если же совпадения нет, программа переходит ко второму шаблону. При наличии совпадения с одним из шаблонов дальнейшее сопостав- ление с шаблонами не выполняется, а осуществляется переход к команде, стоящей за ключевым словом esac. Звездочка *, используемая как шаблон, соответствует любому зна- чению слово, что позволяет выполнять некоторую совокупность ко- манд и в том случае, если не обнаружено совпадение ни с одним из шаблонов. Поэтому, звездочка * в конструкции case должна быть размещена в качестве последнего шаблона (сопоставление с другими шаблонами должно выполняться раньше). Это обеспечивает удобный способ обнаружения ошибочного или непредвиденного выво- да. Шаблоны, указываемые в поле шаблон каждой секции выбора вариан- та, могут содержать метасимволы *, ? или [] для задания расши- рения имени файла, как описано выше в этой же главе. Это дает определенную гибкость. Хорошим примером использования конструкции case...esac служит shell-программа set.term. Она устанавливает shell-переменную TERM в соответствии с типом используемого терминала с помощью следующей командной строки: |TERM=имя_терминала В показанном ниже примере терминалом является один из: Teletype 4420, Teletype 5410 или Teletype 5420. Программа set.term сначала проверяет, имеет ли переменная term значение 4420. Если это так, она присваивает переменной TERM значение T4 и завершается. Если же значение переменной term не равно 4420, программа делает проверку для других значений (5410 и 5420). Затем она выполняет команды, относящиеся к тому шабло- ну, для которого обнаружено совпадение, после чего переходит к первой команде за ключевым словом esac. За шаблонами указанных терминалов в конструкции case...esac включен шаблон *, соответствующий всем остальным типам термина лов. С помощью этого шаблона программа сделает предупреждение пользователю о задании неправильного типа терминала и позволи выйти из конструкции case: |$ cat set.term |echo Если у Вас терминал TTY 4420, введите 4420 |echo Если у Вас терминал TTY 5410, введите 5410 |echo Если у Вас терминал TTY 5420, введите 5420 |read term |case $term | in | 4420) | TERM=T4 | ;; | 5410) | TERM=T5 | ;; | 5420) | TERM=T7 | ;; | *) | echo Неверный тип терминала | ;; |esac |export TERM |echo Конец программы |$ Обратите внимание на использование команды export. Команда ex- port применяется для того, чтобы сделать доступным значение пе- ременной внутри окружения пользователя и для других shell-про- цедур. Что произошло бы, если бы шаблон * был помещен первым? В этом случае программа set.term никогда не присвоила бы значение пе- ременной TERM, поскольку она всегда сопоставляла бы значение переменной term с первым шаблоном *, который соответствует про- извольной цепочке символов. В таблице приводится краткое описание формата и возможностей shell-программы set.term. | +---------------------------------------------------------------+ | | Краткое описание shell-программы | | | | | | set.term - присвоить значение переменной TERM | | | (значение определяется пользователелем) | | +---------------------------------------------------------------| | | команда аргументы | | +---------------------------------------------------------------| | | set.term вводятся в интерактивном | | | режиме | | +---------------------------------------------------------------| | | Описание: Присваивает значение shell-переменной TERM | | | и затем делает доступным это значение другим | | | shell-процедурам. | | | | | | Примечание: Эта команда запрашивает специфический код | | | терминала для сопоставления его с шаблонами | | | в конструкции case. | | +---------------------------------------------------------------+ 5.3.3.7 Безусловные операторы управления: команды break и continue Команда break безусловно завершает выполнение тела цикла или ветви программы, в которой она содержится, а управление переда- ется команде, следующей за оператором done, fi или esac. Если после этих операторов команды отсутствуют, программа завершает- ся. В рассмотренной ранее программе set.term, для выхода из ветви выбора варианта с шаблоном * вместо команды echo, можно было бы использовать команду break: |$ cat set.term |echo Если у Вас терминал TTY 4420, введите 4420 |echo Если у Вас терминал TTY 5410, введите 5410 |echo Если у Вас терминал TTY 5420, введите 5420 |read term |case $term | in | 4420) | TERM=T4 | ;; | 5410) | TERM=T5 | ;; | 5420) | TERM=T7 | ;; | *) | break | ;; |esac |export TERM |echo Конец программы |$ Командой continue обеспечивается переход к следующей итерации в цикле while или for, без выполнения при этом оставшихся команд тела цикла. 5.3.4 Программы отладки Иногда у пользователя может возникнуть потребность в отладке программы с целью поиска и исправления ошибок. У команды sh су- ществуют две опции, которые помогают отлаживать программу: |sh -v имя_shell_программы выводит строки shell-программы по мере их чтения системой |sh -x имя_shell_программы выводит команды и их аргументы по мере выполнения команд Для рассмотрения действия этих опций, создадим shell-программу, которая содержит ошибку, и назовем ее bug: |$ cat bug |today=`date` |echo Введите имя |read person |mail $1 |$person |После выхода из системы зайдите, пожалуйста, |в мою контору. |$today |MLH |$ Заметим, что значение переменной today равно выводу команды da te, которая для выполнения подстановки своего результата должна быть заключена в обратные кавычки. Электронная почта, которая посылается пользователю с именем Том ($2), имеющему входное имя tommy ($1), должна быть выведена в следующем виде: |$ mail |From mlh Thu Apr 10 11:36 CGT 1989 |Том |После выхода из системы зайдите, пожалуйста, |в мою контору. |Thu Apr 10 11:36:32 CGT 1989 |MLH |? Если попытаться выполнить shell-программу bug, то для ее завер- шения придется нажать клавишу BREAK или DELETE. Выполним отладку этой программы, используя команду sh -v. При этом будут выводиться строки файла shell-программы по мере их чтения системой и последующего выполнения: |$ sh -v bug |today=`date` |echo Введите имя |Введите имя |read person |Том |mail $1 Обратите внимание на то, что вывод заканчивается на команде ma- il, поскольку возникает проблема при ее выполнении. Здесь необ- ходимо использовать подстановку строки в команду, чтобы переад- ресовать ввод на команду mail. Прежде чем исправить shell-программу bug, попытаемся ее выпол- нить, используя sh -x, которая выводит команды и их аргументы по мере того, как они читаются системой: |$ sh -x bug Том tommy |+date |today=Thu Apr 10 11:07:23 CST 1989 |+echo Введите имя |Введите имя |+read person |Том |+mail tommy |$ И опять программа останавливается на команде mail. Заметим, что подстановка результата выполнения команды в качестве значения переменной сделана, и это отражено в выводе. Правильная программа bug имеет следующий вид: |$ cat bug |today=`date` |echo Введите имя |read person |mail $1 < и echoe. -tabs Эта опция сохраняет действие табуляции при вы- воде. Символ табуляции при этом заменяется на восемь пробелов (это число устанавливается по умолчанию). Число пробелов, заменяющих симво табуляции, может быть изменено. (Более подроб- ные сведения см. в разд. stty(1) Справочника пользователя.) erase<^h> Эта опция позволяет использовать для удаления символа клавишу стирания вместо задаваемой по умолчанию клавиши #. Обычно клавишей стирания служит клавиша BACKSPACE. echoe В случае использования видеотерминала эта опция позволяет стереть символы с экрана при их уда- лении с помощью клавиши BACKSPACE. Если пользователь собирается использовать эти опции с командой stty, он может включить соответствующие командные строки в свой файл .profile точно так же, как он сделал бы это в случае shell-программы. Применив команду tail, которая выводит нес- колько последних строк файла, можно увидеть результат добавле ния этих четырех командных строк (включая и строку, начинающую- ся с команды echo) в файл .profile: |$ tail -4 .profile |echo Доброе утро! Я готова работать с Вами. |stty -tabs |stty erase <^h> |stty echoe |$ В таблице приводится краткое описание формата и возможностей команды tail. | +---------------------------------------------------------------+ | | Краткое описание команды | | | | | | tail - вывести последнюю часть файла | | +---------------------------------------------------------------| | | команда опции аргументы | | +---------------------------------------------------------------| | | tail -n имя_файла | | +---------------------------------------------------------------| | | Описание: Выводит последние строки файла. | | | | | | Опции: Чтобы задать вывод n строк, следует | | | использовать опцию -n (по умолчанию | | | выводится десять строк). Вместо строк | | | можно указать число блоков (-nb) или | | | символов (-nc). | | +---------------------------------------------------------------+ 5.4.3 Создание каталога rje В этой главе часто шла речь о возможности использования ряда полезных программ какого-либо пользователя всеми пользователями системы. Аналогично другие пользователи могут иметь программы или файлы, которыми они захотят поделиться с Вами. Чтобы они без затруднений могли пересылать Вам файлы, необходимо создать каталог rje (сокращение от "remote job entry"): |$ mkdir rje |$ chmod go+w rje Заметим, что права доступа к каталогу следует изменять с по- мощью команды chmod. Если у пользователя есть каталог rje, с верно установленными правами доступа, то другие пользователи смогут пересылать ему файлы, используя команду uucp. Более под- робные сведения о команде uucp см. в разд. uucp(1) Справочника пользователя. 5.4.4 Использование shell-переменных Некоторые переменные, зарезервированные интерпретатором shell, используются в файле .profile пользователя. Вывести текущее значение любой shell-переменной можно с помощью следующей ко- манды: |echo $имя_переменной Ниже рассматриваются четыре наиболее важные shell-переменные. HOME Эта переменная определяет маршрутное имя входного ката- лога пользователя. Перейдите с помощью команды cd в свой входной каталог, а потом введите: |pwd Что было ответом системы? Теперь введите: |echo $HOME Ответ системы был таким же, что и на команду pwd? $HOME является аргументом, который задается в команде cd по умолчанию (если в этой команде не указан каталог она перемещает пользователя в каталог $HOME). PATH Эта переменная определяет маршрут при поиске и выполне- нии команд. Чтобы посмотреть текущее значение перемен- ной PATH, пользователю следует ввести команду: |echo $PATH Ответом системы будет текущее значение переменной PATH. |$ echo $PATH |:/mylogin/bin:/bin:/usr/bin:/usr/lib |$ Двоеточие (:) служит разделителем между маршрутными именами в цепочке символов, присвоенной переменной PATH. Если перед двоеточием ничего не указано, то име- ется в виду текущий каталог. Обратите внимание, что в последнем примере система осуществляет поиск команд сначала в текущем каталоге, затем в каталоге /mylogin/ bin, затем в каталоге /bin, затем в каталоге /usr/bin и, наконец, в каталоге /usr/lib. Если пользователь работает над программным проектом с другими пользователями, у него может возникнуть желание создать для этой группы разработчиков каталог /bin, ко- торый содержит набор специальных shell-программ, ис пользуемых только участниками проекта. Этот каталог мог бы иметь, например, маршрутное имя /project/bin. После создания такого каталога пользователю следует отредак- тировать свой файл .profile, добавив в конец значения переменной PATH цепочку символов :/project/bin: PATH=:/mylogin/bin:/bin:/usr/lib:/project/bin TERM Эта переменная определяет тип используемого терминала. Чтобы присвоить значение переменной TERM, необходимо выполнить следующие три команды в указанном порядке: |TERM=имя_терминала |export TERM |tput init Совокупность первых двух строк необходима для того, чтобы сообщить системе о типе используемого терминала. Последняя строка, содержащая команду tput, указывает терминалу на то, что ЭВМ ожидает установления связи с определяемым переменной TERM типом терминала. Следова- тельно, эта команда всегда должна вводиться после пере дачи переменной TERM. Чтобы не устанавливать переменную TERM при каждом входе в систему, рассмотренные три командные строки следует включить в свой файл .profile; тогда они автоматически будут выполняться при каждом входе в систему. Поясне- ния, касающиеся способа определения имени терминала для присваивания значения переменной TERM, содержатся в Приложении D "Установка характеристик терминала". В этом же приложении приводятся сведения о команде tput. Если пользователь входит в систему с разных типов тер- миналов, ему было бы полезно в своем файле .profile иметь команду set.term. PS1 Эта переменная устанавливает цепочку символов для пер- вичного приглашения shell (по умолчанию используется знак $). Пользователь может изменять вид приглашения, меняя значение переменной PS1 в своем файле .profile. Обратите внимание на то, что для использования пригла шения, состоящего из нескольких слов, фразу следует заключать в кавычки. В качестве примера включите в свой файл .profile следующее присваивание значения перемен- ной PS1: |PS1="Your command is my wish" ("Ваша команда соответствует моему желанию") После этого с помощью команды . выполните свой файл .profile и посмотрите, каким будет знак приглашения: |$ . .profile |Your command is my wish Обычный знак $ в качестве приглашения исчезает навсегда или по крайней мере до тех пор, пока из файла .profile не будет удалена переменная PS1. 5.5 УПРАЖНЕНИЯ ПО ПРОГРАММИРОВАНИЮ НА ЯЗЫКЕ SHELL 2-1. Создайте shell-программу с именем time используя следую- щую командную строку: |banner `date | cat -c12-19` 2-2. Напишите shell-программу, которая будет выводить плакат- ными буквами только дату. Будьте внимательны, чтобы прог рамма не получила имя какой-либо из команд системы UNIX. 2-3. Напишите shell-программу, которая будет посылать сообще- ние некоторому числу пользователей Вашей системы UNIX. 2-4. Переадресуйте вывод команды date, который не содержит время, на файл. 2-5. Выведите фразу Дорогой коллега в файл, содержащий вывод команды date, из которого не исключена дата. 2-6. Используя уже рассмотренные упражнения, напишите shell-п- рограмму, которая перешлет сообщение, всем упоминавшимся в упражнении 2-3, пользователям Вашей системы. Включите в свое сообщение: - текущую дату и слова Дорогой коллега в заголов- ке сообщения; - основную часть (тело) сообщения, хранящуюся в существующем файле; - завершающее предложение. 2-7. Каким образом можно ввести значения переменных в програм- му mv.file? 2-8. С помощью цикла for переместите группу файлов из текущего каталога в какой-либо другой каталог. Как можно перемес- тить все Ваши файлы в другой каталог? 2-9. Как можно изменить программу search так, чтобы она выпол- няла поиск в нескольких файлах? Подсказка: |for file |in $* 2-10. Установите опции команды stty для своего окружения. 2-11. Измените свое приглашение на слово Hello. 2-12. Проверьте значения переменных $HOME, $TERM и $PATH своего окружения. 5.6 ОТВЕТЫ К УПРАЖНЕНИЯМ 5.6.1 Ответы к упражнениям на использование командного языка shell 1-1. Знак * в начале имени файла подразумевает все файлы с именами, которые заканчиваются на указанную цепочку сим- волов, включая и саму эту цепочку как имя файла. |$ ls *t |cat |123t |new.t |t |$ 1-2. Результатом выполнения команды cat [0-9]* будет следующий вывод: |1memo |100data |9 |05name Команда echo * выведет список всех файлов, содержащихся в текущем каталоге. 1-3. Знак ? можно поместить в любую позицию имени файла. 1-4. Команда ls [0-9]* выведет список только тех файлов, имена которых начинаются с цифры. Команда ls [a-m]* выведет список только тех файлов, имена которых начинаются с букв от "a" до "m" включительно. 1-5. Если Вы запустили последовательность команд, содержащихся в одной командной строке, в фоновом режиме, система сразу же отвечает выводом номера PID этого задания. Нет, знак & (амперсанд) должен быть помещен в конце ко- мандной строки. 1-6. Командная строка должна быть следующей: |cd;pwd > proba;ls >> proba;rk trial 1-7. Измените в командной строке опцию -c: |banner `date | cut -c1-10` 5.6.2 Ответы к упражнениям по программированию на языке shell 2-1. |$ cat time |banner `date | cut -c12-19` |$ chmod u+x time |$ time |(Команда banner выводит время 10:26) |$ 2-2. |$ cat mydate |banner `date | cut -c1-10` |$ 2-3. |$ cat tofriends |echo Введите имя файла, содержащего сообщение. |read note |mail janice marylou bryan < $note |$ Но если Вы использовали для задания входных имен не са- ми входные имена пользователей, а параметры в вызове shell-программы tofriends, то программа могла бы иметь следующий вид: |$ cat tofriends |echo Введите имя файла, содержащего сообщение. |read note |mail $* < $note |$ 2-4. |date | cut -c1-10 > file1 2-5. |echo Дорогой коллега >> file1 2-6. |$ cat send.memo |date | cut -c1-10 > memo1 |echo Дорогой коллега >> memo1 |cat memo >> memo1 |echo Послание от М.Л.Келли >> memo1 |mail janice marylou bryan < memo1 |$ 2-7. |$ cat mv.file |echo Введите маршрутное имя каталога |read path |echo Введите имена файлов, завершите с помощью <^d> | while | read file | do | mv $file $path/$file | done |echo Все сделано |$ 2-8 |$ cat mv.file |read path |for file in $* | do | mv $file $path/$file | done Командная строка для перемещения всех файлов, содержа- щихся в текущем каталоге: |$ mv.file * 2-9. Смотрите подсказку, сделанную в условии к упражнению 2-9. |$ cat search |for file | in $* | do | if grep $word $file > /dev/null | then echo Слово $word содержится в файле $file | else echo Слово $word ОТСУТСТВУЕТ в файле $file | fi | done |$ 2-10. Добавьте к своему файлу .profile следующие командные строки: |stty -tabs |stty erase <^d> |stty echoe 2-11. Добавьте к своему файлу .profile следующие командные строки: |PS1=Hello |export PS1 2-12. Для проверки значений переменных HOME, TERM и PATH сво- его окружения выполните команды: |$ echo $HOME |$ echo $TERM |$ echo $PATH