Главная » 2013 » Декабрь » 12 » 19.7. Интерпретатор команд bash
02:06
19.7. Интерпретатор команд bash

Интерпретатор команд – это программа, выполняющая команды пользователя. Стандартным интерпретатором (или оболочкой) является bash (Bourne Again Shell). Достаточно распространенными также являются следующие интерпретаторы: sh, ash, bsh, tcsh, csh, zsh. Список установленных в вашей системе оболочек находится в файле /etc/shells. Команды оболочки можно вводить в командной строке, а можно оформить в виде сценария. Сценарий – это файл, содержащий команды оболочки. Создайте обыкновенный текстовый файл и сделайте его исполнимым. Система выполнит указанную последовательность команд. Для того, чтобы система узнала, какую оболочку нужно использовать, первая строка сценарий должна содержать полное имя сценария.

Например:

Между символами # и! не должно быть пробелов. Для обработки сценария вы можете использовать любую программу (естественно, она должна понимать синтаксис файла), а не только указанную в файле /etc/shells. Например, вы можете написать:

Оболочка при этом запустит программу /usr/bin/my_proga и передает имя файла сценария в качестве параметра. Если вы напишете:

То оболочка выполнит команду:

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

Теперь рассмотрим все по порядку. С первой строкой, я думаю, все ясно. Вторая строка очищает экран. Третья строка выводит информацию, которая передана сценарию как первый (1) параметр. Запустите сценарий следующим образом:

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

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

При этом не нужно явно указывать три параметра, просто интерпретатор не будет использовать пропуск для разделения параметров и все, что вы передадите ему, будет считаться одним параметром. При этом, если вы запустите сценарий с параметром Vasya Pupkin, на экране увидите Vasya Pupkin. А если укажете параметр Ivanov Ivan Ivanovich, сценарий так и напечатает Ivanov Ivan Ivanovich.

19.7.1. Каналы и списки

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

С помощью каналов вы можете перенаправить стандартный вывод одной программы на стандартный ввод другой. Например:

В первом случае стандартный вывод команды cat (содержимое файла /var/log/secure) перенаправляется на стандартный ввод программы less, которая обеспечивает поэкранный вывод информации. Вторая строка выводит список всех процессов, принадлежащих данному пользователю. Первая команда ps‑ax выводит список всех запущенных в системе процессов, вторая (grep "$UID") ищет фрагмент текста, содержащий идентификатор пользователя (UID) и выводит результат на стандартный вывод, то есть на стандартный ввод команды less. $UID является переменной окружения, которая содержит идентификатор пользователя. О переменных окружения поговорим немного позже.

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

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

Сначала выполняется команда ps для печати заголовка таблицы, а потом – для вывода информации о демоне httpd.

Можно использовать операции конъюнкции и дизъюнкции, например:

Команда Command1 будет выполнена в случае успешного завершения команды Command1 (возвратный код равен 0). Команда Command4 будет выполнена, если код возврата команды Command3 не равен 0. Самый простой пример – создание и изменение каталога:

Обычно интерпретатор bash выполняет команды в синхронном режиме, то есть после запуска программы ожидает ее завершения. Однако можно запустить программу и в асинхронном режиме, то есть без ожидания ее завершения. Для этого нужно использовать символ «&» в конце команды, например:

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

Перенаправление ввода/вывода уже рассматривалось в гл. 5, поэтому я лишь напомню общий формат команд:

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

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

19.7.3. Подоболочки

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

При запуске программа выведет на экран:

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

Сценарий 1 выведет на экран следующую информацию:

А сценарий 2:

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

Сценарий выведет на экран:

19.7.4. Переменные и массивы

Пример простейшей переменной мы уже рассмотрели выше. Переменные в bash не нуждаются в предварительном описании, как в других языках, например, в том же Pascal. Все переменные в bash – текстовые. Имя переменной должно начинаться с буквы и может состоять из латинских букв, цифр, знака подчеркивания. Нельзя определять функцию и переменную с одинаковыми именами. Чтобы воспользоваться значением переменной, нужно использовать символ доллара перед именем переменной:

Как я уже говорил, все переменные текстовые. Например, когда вы присваиваете переменной значение VAR=13, то это будет не числовое значение, а строка из двух символов. Если присваиваемое значение имеет пробелы, его нужно взять в кавычки:

Присвоить значение переменной можно с помощью встроенной команды read:

Параметр –n команды echo не выводит символ новой строки в конце сообщения «Enter value:», то есть не переводит строку. Команда read читает значение, введенное пользователем с клавиатуры, и записывает его в переменную х. Последняя команда выводит только что введенное значение. При использовании команды read можно указывать несколько переменных:

Пользователь должен ввести значения переменных, разделяя их пробелами. Для окончания ввода нужно нажать «Enter». Если введено меньше значений, чем нужно, оставшимся переменным будет присвоена пустая строка. А если больше, то лишние значения будут проигнорированы.

Интерпретатор bash использует следующие метасимволы, имеющие для него особое значение:

Для того, чтобы использовать эти символы как они есть, нужно их цитировать с помощью символа \. Например, символ перевода строки можно цитировать как \n, символ табуляции как \t, а символ вопроса как \?

Особое значение при присваивании переменным значений имеют кавычки. Все символы, заключенные в одинарные кавычки ' ' представляют самих себя. Между двойными кавычками " " выполняются команды и подстановки значений. Символы "\", ",", " ' ", "$" могут цитироваться с помощью обратной наклонной черты: \\, \$, \'

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

Специальные переменные Таблица 19.36

Название переменной

Значение

$0

Имя выполняемой команды. Для сценария – путь, указанный при его вызове. Если вы знакомы с языком программирования Pascal, эта переменная должна вам напоминать вызов функции ParamStr(0)

$n (где n – номер параметра, $1, $2, …)

Обращение к параметру с номеров n. Для Pascal – это вызов функции ParamStr(n)

$#

Число параметров, которые были указаны при вызове сценария. Аналогична вызову функции ParamCount в Pascal

$*

Все параметры, заключенные в кавычки: "$1 $2 …"

$@

Все параметры, каждый из которых заключен в кавычки "$1" "$2" …

$?

Код завершения последней команды

$$

Номер текущего процесса (PID)

$!

Номер(PID) последнего асинхронного процесса (команды, выполненной в фоновом режиме)

Пример.

Интерпретатор bash поддерживает одномерные массивы с неограниченным числом элементов. В других оболочках существуют определенные ограничения на массивы, например, в ksh максимальное число элементов массива ограничено 1024‑мя элементами.

Присвоить значение элементу массива можно с помощью такой конструкции:

Например:

Нумерация элементов начинается с ноля. Тип элементов массива, как и тип переменных, текстовый. Присвоить значение элементам массива можно также с помощью инструкции set. Например, выражение:

Аналогично выражениям:

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

Обратиться к значению элемента можно следующим образом:

Например, вывести значение первого (нулевого) элемента массива можно так:

Обратиться ко всем элементам массива сразу можно с помощью одного из выражений:

Или

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

Особое значение имеют переменные окружения. У каждого процесса есть своя среда – множество доступных ему переменных. Обыкновенные переменные доступны только из локального процесса. Чтобы использовать их значения в порождаемых процессах, нужно использовать встроенную команду export. После того, как вы экспортируете переменные, они будут доступны всем порожденным процессам.

Выражение export:

Как видите, можно экспортировать уже инициализированную переменную (которой уже присвоено значение), а можно выполнить инициализацию переменной непосредственно при экспорте. Экспортировать можно как одну переменную, так и целый список.

Для деактивизации переменной используется команда unset: unset имя

Каждому процессу доступны переменные оболочки, приведенные в табл. 19.37.

Переменные оболочки Таблица 19.37

Название переменной

Значение

PWD

Текущий каталог

UID

Идентификатор пользователя, запустившего сценарий

REPLY

Последняя строка, введенная с помощью команды read

RANDOM

Случайное число в диапазоне от 0 до 32767

SECONDS

Число секунд, прошедшее с момента запуска оболочки

IPS (Internal Filed Separator)

Внутренний разделитель полей. Используется синтаксическим анализатором и командой read для разделения строчки на слова. По умолчанию его значение равно " \t\n", где: « » – пробел; «\t» – символ табуляции; ,«\n» – символ новой строки

HOME

Домашний каталог

PATH

Путь вызова

LOGNAME

Имя пользователя, которое использовалось для входа в систему

MAIL

Имя файла, в который поступает электронная почта

SHELL

Имя интерпретатора команд

TERM

Тип терминала пользователя

Пример.

19.7.5. Подстановка команд и арифметических выражений

В гл. 13 (п. 13.5) мы уже сталкивались с подстановкой команд. Тогда переменной DT присваивался результат выполнения команды date:

Как я уже писал, при подстановке команд нужно использовать обратные одинарные кавычки (они расположены под символом тильды на клавиатуре). Подставлять можно не только одну команду, а целые списки команд:

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

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

Данная команда ищет в файле /etc/passwd вхождение результата выполнения команды id –un

Подстановка арифметических выражений осуществляется с помощью конструкции $(( выражение )) Например:

При этом на экране вы увидите число 7, а не 7.5, потому что используется целочисленное вычисление. Пример. Количество часов, прошедшее с момента запуска оболочки:

19.7.6. Управляющие структуры и циклы

К управляющим структурам относятся:

• Конструкция if‑fi.

• Конструкция case‑esac.

Конструкция if‑fi

Общий синтаксис конструкции if‑fi:

Конструкция if‑fi работает так же, как и в других языках программирования. Если Список1 (условие) истинный, выполняется Список1, иначе выполняется Список3 и проверяется его истинность и т. д. Допускается неограниченная вложенность операторов if. Например:

Можно использовать сокращенный вариант:

Например:

Вместо списка команд удобно использовать команду test или выражение [условие]. Например, следующие выражения аналогичны:

И первое, и второе выражение проверяют существование файла /etc/passwd. Другие опции команды test представлены в табл. 19.38.

Опции команды test Таблица 19.38

Опция

Возвращаемое значение и описание

‑d файл

Истина, если файл существует и является каталогом

‑е файл

Истина, если файл существует

‑f файл

Истина, если файл существует и является простым файлом

‑k файл

Истина, если файл существует и для него установлен бит односторонней операции

‑L файл

Истина, если файл существует и является ссылкой

‑r файл

Истина, если файл существует и доступен для чтения

‑s файл

Истина, если файл существует и его размер больше 0

‑х файл

Истина, если файл существует и является исполнимым

‑w файл

Истина, если файл существует и доступен для записи

‑о файл

Истина, если файл существует и принадлежит данному пользователю

‑z строка

Истина, если длина строки равна 0

‑n строка

Истина, если длина строки не равна 0

Команда test, в случае успешного завершения, возвращает значение истина, то есть 0 – успешное завершение. Если в скобках стоит непустое слово, test возвратит тоже 0, например:

В первом случае возвращается истина (true), на экран выводится ноль – код удачного (безошибочного) завершения программы. Во втором случае на экран выводится единица – команда test возвратила значение ложь (false). Сравнение строк осуществляется следующим образом: выражения str1 = str2 или str1 == str2 истинны, когда строки str1 и str2 равны. Обратите внимание: между двумя символами равно не должно быть пропуска!

Символ! инвертирует любое условие команды test, например, выражение str1 != str2 будет истинным, когда строки Str1 и Str2 не равны между собой. Символ! является символом логической операции NOT (отрицание). Кроме этого символа, можно использовать опции команды –о и –а, которые обозначают логические операции ИЛИ (OR) и И (AND). Например:

0

1

В первом случае непустая строка Str возвращает истину, опция –f возвращает также истину, потому что файл /etc/passwd существует всегда. Результат операции И: Истина И Истина = истина, поэтому на экране вы увидите 0.

Во втором случае пустая строка Str возвратит ложь, а опция –f возвращает истину. Результат операции И: Ложь И Истина = ложь. Если вы забыли законы логики, освежите свои знания с помощью табл. 19.39.

Логические операции Таблица 19.39

AND

True

False

OR

True

False 

XOR

True

False

True

True

False 

True

True

True

True

False

True

False

False

False

False

True

False

False

True

False

Операция XOR – это исключающее ИЛИ. Данная операция не используется при создании сценариев с помощью интерпретатора bash.

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

Сравнение целых чисел Таблица 19.40

Опция

Описание

‑eq

Равно

‑ne

Не равно

‑It

Меньше

‑gt

Больше

‑le

Меньше или равно

‑ge

Больше или равно

Интерпретатор bash воспринимает строки, как целые числа. Если нужно обнулить строку, то это достигается таким присваиванием: х=0.

Пример.

Поскольку 111 меньше, чем 124, на экране вы увидите 0 (истина).

Примечание. Во всех примерах, вы, наверное, заметили использование команды export. Это необходимо для того, чтобы порожденному процессу (не забывайте: test – это отдельная программа) переменнаях была доступна.

Теперь, когда мы уже знакомы с конструкциями test и if, рассмотрим небольшой пример, демонстрирующий вложенность операторов if и использование команды test. Пример приведен в листинге 19.2.

Листинг 19.2. Пример вложенности операторов

Если вы введете 5, сценарий отобразит на экране слово «Отлично», при вводе 4 вы увидите слово «Хорошо» и так далее. Если вы введете 0, 1 или число больше пяти, вы увидите на экране последнюю фразу: «Как вообще можно было получить такую оценку???».

Конструкция case‑esac

Конструкция выбора (case – выбор) имеет следующий синтаксис:

Рассмотрим сценарий (см. листинг 19.3), аналогичный сценарию 19.2, но использующий конструкцию case вместо if.

Листинг 19.3. Пример использования оператора case

Работа сценария аналогична первому сценарию: при вводе оценок 2, 3, 4, 5 будут отображены соответствующие сообщения, а во всех остальных случаях – последнее сообщение.

Примечание. Структура оператора case больше напоминает структуру оператора case в языке Pascal, чем в языке С. Последняя строка выбора с шаблоном *) будет выбрана, когда не произойдет ни одного совпадения с ранее указанными шаблонами. Если же произошло совпадение с шаблоном шаблонN, то будет выполнен список списокN. После выполнения списка команд списокN будет произведен выход из структуры case –так же как и в Pascal. В языке С наблюдается нечто другое: если будет обнаружено совпадение, скажем с шаблоном3, то будут выполнены последовательности операторов 3, 4, 5, … N. Чтобы прервать выполнение блока case в языке С нужно использовать оператор break. В bash же такого нет.

Если для одного списка команд нужно описать два или более шаблонов, используется символ | (OR).

Циклы

Интерпретаторы bash и ksh поддерживают циклы for, while, until, select, a интерпретатор sh только for и while.

Синтаксис цикла for:

Простой пример:

На экране вы увидите:

Еще раз напомню, что любой список в bash должен заканчиваться точкой с запятой. Начинающие «программисты» делают много ошибок, связанных именно с этой особенностью списков. Пример использования: построчно вывести содержимое файла /etc/passwd вы можете с помощью такого цикла:

Цикл for закончит свою работу, когда будет обработан последний элемент списка, в данном случае, когда на экран будет выведена последняя строка файла /etc/passwd.

Синтаксис цикла while:

Цикл while будет выполняться, пока условие, заданное в списке Список!,

Будет истинным. Поэтому цикл while иногда называют циклом с истинным условием. Например,

На экране вы увидите:

Когда переменная х примет значение 10, цикл завершит свою работу, так как программа test вернет значение False (x уже не меньше, а равен 10). Цикл until (до) имеет похожую структуру, но выполняется несколько иначе:

Цикл until прекратит работу, когда условие, указанное в списке Список1, станет истинным. Другими словами, он будет выполняться пока это условие ложно. Цикл while, наоборот, выполняется пока условие истинно. Лучше всего разница между этими циклами видна на примере (сравните листинги 19.4 и 19.5)

Листинг 19.4. Цикл while
Листинг 19.5. Цикл until

Циклы, приведенные в листингах 19.4 и 19.5, выведут одинаковую последовательность цифр на экран:

Рассмотрим еще один полезный цикл select, который позволяет создавать нумерованные пункты меню. Его конструкция такова:

Пример:

В моем временном каталоге /home/den/temp находится всего два файла – file. txt, proto. txt, поэтому на экране монитора будет отображено следующее:

Выберите файл для просмотра:

Первые два файла – это ссылки на текущий и родительский каталоги. Пункты меню 3 и 4 – это файлы, которые можно выбрать для просмотра. QUIT – это последний элемент списка. При его выборе сработает оператор break и цикл завершится.

19.7.7. Подстановка переменных

Мы уже рассмотрели подстановку команд, сейчас рассмотрим подстановку переменных (см. табл. 19.41).

Подстановка переменных Таблица 19.41

Конструкция

Описание

${переменная:‑значение}

Если переменная определена и не является пустой строкой, подставляется ее значение, иначе подставляется значение, указанное в конструкции. Реальное значение переменной при этом не изменяется

${переменна:=значение}

Значение присваивается переменной, если она не определена или является пустой строкой

${переменная:?сообщение}

Если переменная не определена или является пустой строкой, выводится указанное сообщение

${переменная:+значение}

Если переменная инициализирована (определена), вместо нее используется указанное в конструкции значение. Реальное значение переменной не изменяется

${переменная}

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

Пример.

Данное сообщение будет выведено, если сценарий будет запущен без параметров. Если указать хотя бы один параметр, сообщение не будет отображаться на экране.

19.7.8. Функции

Описание функции выглядит так:

Пример:

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

На экране вы увидите примерно следующую информацию:

Проанализируем полученную информацию. Как уже отмечалось, функция не порождает нового процесса, поэтому PID остался равным 788 как до вызова функции, так и во время ее выполнения. Переменная X доступна нашей функции, потому что описана до вызова функции. Функция «видит» значение переменной X, установленное в основном блоке сценария. Затем функция изменяет значение переменной X и передает его в основной блок (Х=2). Функции был передан только один параметр – /etc, вместо второго параметра была отображена пустая строка. Имя файла осталось прежним – fn. Обратите внимание на важный момент: функция сообщила нам много полезной информации об устройстве функций в bash, но не оправдала своего названия – cdir (change dir). Реально изменения каталога не произошло, потому что перед выполнением команды cd была выполнена команда return с кодом завершения 0, которая прервала выполнение функции.

19.7.9. Обработка сигналов и протоколирование

Возможно, вы хотите обеспечить выполнение вашего сценария после выхода пользователя из интерпретатора или выполнить какие‑нибудь действия при отключении удаленного пользователя от системы. «Перехватить» сигнал (прерывание) можно с помощью команды trap. Формат команды trap следующий:

Где: имя – это имя функции или набор команд, которые должны быть выполнены при получении сигнала;

Сигналы – наиболее часто используется перехват сигналов, описанных в табл. 19.42. Полный список сигналов вы найдете в гл. 5.

Сигналы Таблица 19.42

Номер

Название

Описание

01

SIGHUP

Освобождение линии (hangup)

02

SIGINT

Прерывание (interrupt)

03

SIGQUIT

Выход (quit)

09

SIGKILL

Уничтожение процесса (kill). He перехватывается и не игнорируется

15

SIGTERM

Программный сигнал завершения

Пример. Игнорирование сигналов 1, 2, 3, 15

: – это пустой оператор, не выполняющий никаких действий.

Рассмотрим, как можно протоколировать работу собственного сценария. Для этого существуют два способа – с помощью команды tee и команды script.

Способ 1:

Способ 2:

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

Категория: Создание пакетов | Просмотров: 504 | Добавил: spb_serge | Рейтинг: 0.0/0
Всего комментариев: 0
Имя *:
Email *:
Код *: