Библиотека сайта rus-linux.net
9.3. Конструкции языка программирования shell
Язык программирования shell имеет несколько конструкций, которые придадут гибкость вашим программам:
- комметнарии позволят описывать функции программы;
- "here document" позволяет вам включать в shell программы строки, которые будут перенаправляться как ввод в некоторые команды shell программы;
- команда exit позволяет завершать программу в нужной точке и использовать коды возврата;
- конструкции цикла for, while позволяют повторять группу команд в цикле;
- условные команды if и case выполняют группу команд, если выполнилось некоторое условие;
- команда break позволяет выполнить безусловный выход из цикла.
9.3.1. Комментарии
Чтобы в программе разместить комментарии, воспользуйтесь знаком #. Если знак # стоит после команды, то сама команда выполняется, а комментарий игнорируется. Формат строки комментария:
#comment<CR>
9.3.2. "Here document"
"Here document" позволяет размещать в shell программе строки, которые перенаправляются в качестве ввода команды в этой программе. Это один из способов обеспечения ввода для команды в shell программе без использования отдельного файла. Запись состоит из символа перенаправления << и разделителя, который указывает начало и конец строк ввода. В качестве разделителя может использоваться один символ или строка символов. Чаще всего это знак !.
Формат команды следующий:
command<<delimiter<CR> ...input lines...<CR> delimiter<CR>
9.3.3. Использование ed в shell программе
"Here document" предлагает способ использования ed в shell программе. Предположим вы хотите создать shell программу, которая будет вызывать редактор ed, проводить глобальные изменения в файле, записывать изменения в файл и затем завершать работу с ed. На следующем экране приведено содержание программы ch.text, которая выполняет эти задачи:
$ cat ch.text<CR> echo Type in the filename read file1 echo Type in the exact text to be changed. read old_text echo Type in the exact new text to replace the above. read new_text ed - $file1 <<! g/$old_text/s//$new_text/g w q ! $
Обратите внимание на знак - (минус) в команде ed. Эта опция предотвращает распечатку счетчика символов на экране. Обратите также внимание на формат команды ed для глобальной замены:
g/$old_text/s//$new_text/g
Программа использует 3 переменные: file1, old_text, new_text. При запуске эта программа использует команду read для получения значений этих переменных. Эти переменные содержат следующую информацию:
file - имя файла, который будет редактироваться;
old_text - текст, который будет изменен;
new_text - новый текст.
Переменные вводятся в программу, here document перенаправляет команду глобальной замены, команду записи и команду завершения команде ed. Запустите программу ch.text. Получите следующий экран:
$ ch.text<CR> Type in the filename memo<CR> Type in the exact text to be changed. Dear John:<CR> Type in the exact new text to replace the above. To what it may concern:<CR> $ cat memo<CR> To what it may concern:<CR> $
9.3.4. Коды завершения
Большинство команд shell возвращает коды, которые указывают, успешно ли завершилась команда. Если возвращаемое значение 0(ноль), то команда выполнилась успешно. Коды возврата не печатаются автоматически, но их можно получить как значение специального параметра shell $?.
9.3.4.1. Проверка кодов завершения
После выполнения в интерактивном режиме команды, вы можете увидеть код завершения при вводе:
echo $?Рассмотрим следующий пример:
$ cat hi This is file hi. $ echo $? 0 $ cat hello cat: cannot open hello $ echo $? 2 $
В первом случае файл hi существует в вашем справочнике и вы имеете разрешение на чтение. С помощью команды cat вы можете распечатать содержимое файла. Результат команды cat: код возврата 0, который вы получите, задав параметр $?. Во втором случае файл либо не существует, либо вы не имеете право на чтение. Команда cat печатает диагностическое сообщение и возвращает код 2.
shell программа нормально завершается, когда выполнится последняя команда в файле. Однако вы можете использовать команду exit для завершения программы. Более важно то, что вы можете использовать команду exit для получения кодов возврата shell программы.
9.3.5. Циклы
Операторы цикла for и while позволяют выполнить команду или последовательность команд несколько раз.
9.3.5.1. Оператор for
Оператор for выполняет последовательность команд для каждого элемента списка. Он имеет формат:
for variable<CR> in a_list_of_values<CR> do<CR> command_1<CR> command_2<CR> . . . last command<CR> done<CR>
Для каждой итерации цикла следующий элемент списка присваивается переменной, данной в операторе for. Ссылка на эту переменную может быть сделана в любом месте в командах внутри оператора do. При конструировании каждой секции команд вам необходимо убедиться, что каждому do соответствует done в конце цикла.
Переменная может иметь любое имя. Например, если ваша переменная названа var, то ссылка в списке команд на $var сделает значение доступным. Если оператор in опущен, то значением для var будет набор аргументов, заданный в команде и доступный в специальном параметре $*. Список команд между ключевым словом do и done будет выполнен для каждого значения.
Когда команды будут выполнены для последнего элемента списка, программа будет выполнять строку ниже done.
9.3.5.2. Оператор while
Оператор цикла while использует 2 группы команд. Он будет выполнять последовательность команд во второй группе (список do ... done) до тех пор пока последняя команда в первой группе (список while) возвращает состояние "истина", означающее, что выражение после do может быть выполнено.
Общий формат оператора цикла while:
while<CR> command_1<CR> . . . last command<CR> do<CR> command_1<CR> . . . last command<CR> done<CR>
Например, программа enter.name использует цикл while для ввода списка имен в файл. Программа состоит из следующих командных строк:
$ cat enter.name<CR> while read x do echo $x>>xfile done $
Внеся некоторые добавления, получим следующую программу:
$ cat enter.name<CR> echo Please type in each person's name and than a <CR> echo Please end the list of names with a <^d> while read x do echo $x>>xfile done echo xfile contains the following names: cat xfile $
Обратите внимание, что после завершения цикла программа выполняет команды ниже done.
В первых двух командах echo используются специальные символы, так что вы должны воспользоваться кавычками для отмены специального значения. На следующем экране приведены результаты выполнения программы enter.name:
$ enter.name<CR> Please type in each person's name and than a <CR> Please end the list of names with a <^d> Mary Lou<CR> Janice<CR> <^d> xfile contains the following names: Mary Lou Janice $
После того, как цикл завершится, программа распечатает все имена, содержащиеся в xfile.
9.3.6. Использование /dev/null
Файловая система имеет файл /dev/null, где вы можете хранить нежелательный вывод. Например, если просто ввести команду who, то система ответит, кто работает в системе. Если вы перенаправите вывод этой команды в /dev/null:
who > /dev/nullто не получите ответа.
9.3.7. Условные операторы
Оператор if ... thenКоманда if говорит shell программе, что нужно выполнить последовательность команд после then, если последняя команда в списке команд конструкции if выполнилась успешно. Конструкции if заканчиваются ключевым словом fi.
Общий формат конструкции if:
if<CR> command_1<CR> . . . last command<CR> then<CR> command_1<CR> . . . last command<CR> fi<CR>
Например, shell программа search демонстрирует применение конструкции if ... then. Программа search использует команду grep для поиска слова в файле. Если grep выполнилась успешно, то программа отображает найденное слово. Экран будет выглядеть следующим образом:
$ cat search<CR> echo Type in the word and the file name. read word file if grep $word $file then echo $word is in $file fi $
Эта программа отображает вывод команды grep. Если вы хотите сохранить ответ системы на команду grep в вашей программе, то воспользуйтесь файлом /dev/null, изменив командную строку if на следующую:
if grep $word $file > /dev/null<CR>
Теперь выполните команду search. Она ответит только сообщением, указанным после команды echo.
Конструкция if ... then ... else может исполнять альтернативный набор команд, стоящий после else, в случае, если последовательность if является ложью. Формат этой конструкции следующий:
if<CR> command_1<CR> . . . last command<CR> .linthen<CR> command_1<CR> . . . last command<CR> else<CR> command_1<CR> . . . last command<CR> fi<CR>
С помощью этой конструкции вы можете усовершенствовать программу search, так что она будет сообщать вам и найденное слово и то, что слово не найдено. В этом случае программа search будет выглядеть следующим образом:
$ cat search<CR> echo Type in the word and the file name. read word file if grep $word $file > /dev/null then echo $word is in $file else echo $word is NOT in $file fi $
Команда test
Команда test используется для организации цикла. Она проверяет на истинность определенные условия и полезна для организации условных конструкций. Если условие истинно, то цикл будет продолжен. Если условие ложно, то цикл завершится и будет выполняться следующая команда. Некоторые примеры использования команды test:
test -r file<CR>
- истина, если файл существует и доступен для чтения;
test -w file<CR>
- истина, если файл существует и доступен для записи;
test -x file<CR>
- истина, если файл существует и является выполняемым;
test -s file<CR>
- истина, если файл существует и имеет как минимум один символ;
test var1 -eq var2<CR>
- истина, если var1 равно var2;
test var1 -ne var2<CR>
- истина, если var1 не равно var2.
Пример. Создадим shell программу, которая перемещает все исполняемые файлы из текущего справочника в ваш справочник bin. Для этого воспользуемся командой test -x для выбора исполняемых файлов. Программа mv.file будет выглядеть следующим образом:
$ cat mv.file<CR> echo type in the directory path read path for file do if test -x $file then mv $file $path/$file fi done $
Конструкция case ... esac позволяет выбрать вам один из несколько шаблонов и затем выполнить список команд для этого шаблона. Выражение-шаблон должно начинаться с ключевого слова in, а правая круглая скобка должна быть помещена после последнего символа каждого шаблона. Последовательность команд для каждого шаблона заканчивается двумя знаками ;;. Конструкция case должна быть закончена ключевым словом esac.
Общий формат конструкции case:
case word<CR> in<CR> pattern1)<CR> command line 1<CR> . . . last command line<CR> ;;<CR> pattern2)<CR> command line 1<CR> . . last command line<CR> ;;<CR> pattern3)<CR> command line 1<CR> . . last command line<CR> ;;<CR> *)<CR> command line 1<CR> . . last command line<CR> ;;<CR> esac<CR>
Конструкция case пытается найти word с шаблоном pattern в первой секции шаблонов. Если поиск удачен, то программа выполняет командные строки после первого шаблона до соответствующих знаков ;;.
Если первый шаблон не найден, то осуществляется переход ко второму шаблону. Если любой шаблон найден, то программа не рассматривает остальные шаблоны, а переходит к команде, следующей за esac. Знак * используется как шаблон для поиска любого word и таким образом дает вам набор команд, который будет выполнен, если никакой другой шаблон не будет найден. Поэтому шаблон звездочка (*) размещается как последний шаблон в конструкции case, чтобы другие шаблоны были проверены первыми. Это поможет вам обнаружить некорректный и неожиданный ввод.
В шаблонах могут использоваться метасимволы *, ?, []. Это обеспечивает гибкость программ.
Рассмотрим пример. Программа set.term устанавливает переменную TERM в соответствии с типом терминала, который вы используете. Применяется следующая командная строка:
TERM=terminal_name<CR>
Шаблон * стоит последним в списке шаблонов. Он выдает предупреждающее сообщение, что для указанного типа терминала нет соответствующего шаблона и позволяет вам завершить конструкцию case.
Пример.
$ cat set.term<CR> echo If you have a TTY 4420 type in 4420 echo If you have a TTY 5410 type in 5410 echo If you have a TTY 5420 type in 5420 read term case term in 4420) TERM-T4 ;; 5410) TERM-T5 ;; 5420) TERM-T7 ;; *) echo not a correcr terminal type ;; esac export TERM echo end of programm $
9.3.8. Безусловная передача управления
Команда break безусловно останавливает выполнение любого цикла, в котором он встречается и передает управление команде, следующей после ключевых слов done, fi или esac.
В предыдущем примере программы set.term вы можете использовать команду break, вместо echo, чтобы выйти из программы, как приведено в следующем примере:
Пример.
$ cat set.term<CR> echo If you have a TTY 4420 type in 4420 echo If you have a TTY 5410 type in 5410 echo If you have a TTY 5420 type in 5420 read term case term in 4420) TERM-T4 ;; 5410) TERM-T5 ;; 5420) TERM-T7 ;; *) break ;; esac export TERM echo end of programm $
Команда continue приведет к тому, что программа немедленно перейдет к следующей итерации цикла while или for без выполнения остальных команд в цикле.