Назад Вернуться к оглавлению Дальше

LinuxCenter
Заказать можно здесь

5. Интерфейс командной строки (продолжение)

5.7. Раскрытие выражений (expansion)
5.8. Shell как язык программирования
5.9. Скрипты
5.10. Некоторые употребительные команды
5.11. Работа с архиваторами

5.7. Раскрытие выражений (expansion)

Когда оболочка получает какую-то командную строку на выполнение, она до начала выполнения команды осуществляет "грамматический разбор" полученной командной строки. Одним из этапов такого "разбора" является раскрытие или подстановка выражений (expansion). В bash имеется семь типов подстановки выражений: раскрытие скобок (brace expansion), замена знака тильды (tilde expansion), подстановка параметров, подстановка переменных, подстановка команд, арифметические подстановки (выполняемые слева направо), разделение слов и раскрытие шаблонов имен файлов и каталогов (pathname expansion). Все эти операции выполняются именно в том порядке, как они здесь перечислены.

Раскрытие скобок

Раскрытие скобок проще всего пояснить на примере. Предположим, что нам нужно создать сразу несколько подкаталогов в каком-то каталоге или поменять владельца сразу у нескольких файлов. Эти действия можно выполнить с помощью следующих команд:
[kos]$ mkdir /usr/local/src/bash/{old,new,dist,bugs}
[root}# chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}
В первом случае в каталоге /usr/local/src/bash/ будут созданы подкаталоги old, new, dist и bugs. Во втором случае владелец будет изменен у файлов
 /usr/ucb/ex
 /usr/lib/ex?.?*
 /usr/ucb/edit
 /usr/lib/ex?.?*
 /usr/ucb/ex
 /usr/lib/how_ex
 /usr/ucb/edit
 /usr/lib/how_ex
То есть для каждой пары скобок генерируются несколько отдельных строк (их число равно числу слов, стоящих внутри скобок) путем приписывания к каждому слову из скобок (спереди) того, что стоит перед скобкой, и приписывания в конец каждого полученного слова того, что стоит после скобки. Еще один пример: строка a{d,c,b}e при раскрытии скобок превращается в три слова `ade ace abe'.

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

Замена тильды (Tilde Expansion)

Если слово начинается с символа тильды (~), все символы до первого слэша (или все символы, если слэша нет) трактуются как имя пользователя (login name). Если это имя есть пустая строка, то тильда заменяется значением переменной HOME. Если значение переменной HOME не задано, тильда заменяется на полный путь к домашнему каталогу пользователя, запустившего оболочку.

Если вслед за знаком тильды стоит знак плюс (+), эти два знака заменяются на полное имя текущего каталога (то есть значение переменной PWD). Если за знаком тильды следует дефис (-), подставляется значение переменой OLDPWD.

Если вслед за знаком тильды (до слэша) стоит слово, совпадающее с именем одного из легальных пользователей, тильда и имя пользователя заменяются полным именем домашнего каталога этого пользователя. Если слово, следующее за тильдой, не является именем пользователя (и не пусто), то оно остается неизменным.

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

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

${parameter}

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

Отдельно надо рассмотреть вопрос о том, каким образом раскрываются специальные параметры (присваивать им значения, как Вы помните, нельзя).

* Заменяется позиционными параметрами, начиная с первого. Когда производится подстановка внутри двойных кавычек, этот параметр заменяется на одно слово, состоящее из всех позиционных параметров, разделенных первым символом переменой IFS. Другими словами "$*" эквивалентно "$1c$2c...", где с - это первый символ переменной IFS. Если IFS не задана или ее значение есть нулевая строка, то параметры разделяются символами пробела.
@ Заменяется позиционными параметрами, начиная с первого. Когда производится подстановка внутри двойных кавычек, каждый параметр раскрывается как отдельное слово. То есть "$@" эквивалентно "$1" "$2" ... Если позиционных параметров нет, "$@" и $@ просто удаляются.
# Заменяется на число позиционых параметров в десятичной записи.
? Заменяется на значение статуса выхода последнего выполнявшегося конвейера.
-
(дефис)
Заменяется набором текущих опций, которые были установлены при запуске оболочки, с помощью встроенной команды set или самой оболочкой (например, флаг -i).
$ Заменяется идентификатором процесса (PID) оболочки. В подоболочке (subshell) заменяется идентификатором процесса текущей оболочки, а не подоболочки.
! Заменяется идентификатором процесса (PID) последней из выполняющихся фоновых (asynchronous) команд.
0 Заменяется именем оболочки или скрипта (попробуйте дать команду echo $0). Этот параметр устанавливается при инициализации оболочки. Если оболочка bash была инициализирована с параметром, являющимся именем файла с командами (скриптом), то значение $0 задается равным имени этого файла. Если bash запущена с опцией -c, то $0 устанавливается равным первому аргументу после строки, которая должна быть выполнена, если таковая существует. В противном случае этот параметр задается равным полному пути к запущенной оболочке, так же как и в случае, когда оболочка запускалась без опций и аргументов.
_
(подчеркивание)
Заменяется на последний аргумент предыдущей команды (при этом в этом аргументе производятся все положенные замены).

Переменные являются частным случаем параметров. Все значения переменных подвергаются подстановке знака тильды, раскрытию параметров и переменных, подстановке команд, подстановкам арифметических выражений, а также удалению специальных символов \, ` и " (смотри ниже). Разделение слов не производится, за исключением случая "$@" (объяснение смотри в табличке специальных параметров). Раскрытие шаблонов имен файлов и каталогов не производится.

Подстановка команд

Подстановка команд является очень мощным инструментов bash. Она заключается в замене имени команды на результат ее выполнения. Существует две формы подстановки команд:

$(command)

и

`command`

Если применяется вторая из этих форм, то обратный слэш внутри кавычек трактуется как литерал, кроме тех случаев, когда за ним следует $, `, или \. Если же используется форма $(command), все символы внутри скобок составляют команду и ни один из них не считается специальным символом.

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

Арифметичекие подстановки (Arithmetic Expansion)

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

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

Синтаксис выражения expression подобен синтаксису арифметических выражений в языке C, подробнее об этом можно прочитать в разделе ARITHMETIC EVALUATION man-страницы по каманде bash. Например, команда

[kos] $ echo $(( 2 + 3 * 5 ))
в качестве результата выдает "17".

Если выражение некорректно, bash выдает сообщение об ошибке.

Разделение слов (word splitting)

После завершения подстановок параметров, команд и арифметических выражений оболочка снова анализирует командную строку (в том виде, который она приобрела к этому моменту) и осуществляет разделение слов (word splitting).

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

Заметим, что если в командной строке не производилось никаких подстановок, то разбиение на слова не производится.

Раскрытие шаблонов имен файлов и каталогов (Pathname Expansion)

Подстановки имен путей и файлов (Pathname expansion) используются для того, чтобы с помощью краткого образца или шаблона указать несколько имен файлов (или каталогов), соответствующих данному шаблону. После разделение слов, если не была задана опция -f, bash производит поиск в каждом слове командной строки символов *, ?, and [ . Если будет найдено слово с одним или несколькими вхождениями таких символов, то это слово рассматривается как шаблон, который должен быть заменен словами из лексикографически упорядоченного списка имен путей, соответствующих данному шаблону. Если имен, соответствующих шаблону, не найдено, и переменная allow_null_glob_expansion не задана, слово не изменяется. Если эта переменная установлена, а соответствующих путей не найдено, слово удаляется из командной строки.

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

Специальные символы шаблонов имеют следующее значение:

* Соответствует произвольной строке символов, включая пустую строку. Например, my*.txt будет заменено на myday.txt, myweek.txt и mymonth.txt (если такие файлы существуют), а *.jpg соответствует всем файлам с расширением jpg в указанном каталоге.
? Соответствует любому одиночному символу. Например, вместо шаблона file?.txt будут подставлены имена file1.txt и filex.txt, но не file10.txt.
[...] Соответствует любому символу из числа символов, указанных в скобках. Пары символов, разделенные знаком минуса, обозначают интервал; любой символ стоящий лексически между этими двумя символами, включая и символы, задающие интервал, соответствует шаблону. Если первым символом внутри скобок является ! или a ^ , то считается, что шаблону (в данной позиции) соответствуют все символы, не указанные в скобках.

Шаблоны имен файлов очень часто применяется в командных строках, содержащих команду ls. Представьте себе, что Вы хотите просмотреть информацию о содержимом каталога, в котором находится огромное количество разных файлов различных форматов, например, файлов с изображениями форматов gif, jpeg, avi и так далее. Чтобы получить только список файлов формата jpeg, Вы можете использовать команду

[kos] $ ls *.jpg
Если в каталоге имеется множество файлов, имена которых представлены четырехзначными номерами, то аналогичной командой можно вывести только список файлов с номерами от 0200 до 0499:
[kos] $ ls -l 0[2-4]??.*

Удаление специальных символов

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

5.8. Shell как язык программирования

Как уже говорилось выше, для построения произвольных алгоритмов необходимо иметь операторы проверки условий. Оболочка bash поддерживает операторы выбоа if then else и case, а также операторы организации циклов for, while, until, благодаря чему она превращается в мощный язык программирования.

Операторы if и test (или [ ])

Конструкция условного оператора в слегка упрощенном виде выглядит так:

if list1 then list2 else list3 fi,
где list1, list2 и list3 - это последовательности команд, разделенные запятыми и оканчивающиеся точкой с запятой или символом новой строки. Кроме того, эти последовательности могут быть заключены в фигурные скобки: { list }.

Оператор if проверяет значение, возвращаемое командами из list1. Если в этом списке несколько команд, то проверяется значение, возвращаемое последней командой списка. Если это значение равно 0, то будут выполняться команды из list2, если это значение не нулевое, будут выполнены команды из list3. Значение, возвращаемой таким составным оператором if, совпадает со значением, выдываемым последней командой выполняемой последовательности.

Полный формат команды if имеет вид:

if list then list [ elif list then list ] ... [ else list ] fi
(здесь квадратные скобки означают только необязательность присутствия в операторе того, что в них содержится).

В качестве выражения, которое стоит сразу после if или elif, часто используется команда test, которая может обозначаться также квадратными скобками [ ]. Команда test выполняет вычисление некоторого выражения и возвращает значение 0, если выражение истинно, и 1 в противном случае. Выражение передается программе test как аргумент. Вместо того, чтобы писать

test expression,
можно заключить выражение в квадратные скобки:
[ expression ].
Заметьте, что test и [ - это два имени одной и той же программы, а не какое-то магичекое преобразование, выполняемое оболочкой bash (только синтаксис [ требует, чтобы была поставлена закрывающая скобка). Заметьте также, что вместо test в конструкции if может быть использована любая программа.

Вот несколько примеров построения выражений для оператора test:

  • expression1 -a expression2
    Булевский оператор AND (И). Верен, если верны оба выражения.
  • expression1 -o expression2
    Булевский оператор OR (ИЛИ). Верен, если верно любое из двух выражений.
  • string1 = string2
    Сравнивает две строки на совпадение.
  • -e file
    Верно, если файл существует.
  • -d file
    Верно, если file существует и является каталогом.
В заключение приведем пример использования оператора if:
if [ -e textmode2.htm ] ; then
	ls textmode*
else
	pwd
fi

Оператор case

Формат оператора case таков:
case word in [ pattern [ | pattern ] ... ) list ;; ] ... esac
Команда case вначале производит раскрытие слова word, и пытается сопоставить результат с каждым из образцов pattern по-очередно. После нахождения первого совпадения дальнейшие проверки не производятся, выполняется список команд, стоящий после того образца, с которым обнаружено совпадение. Значение, возвращаемое оператором, равно 0, если совпадений с образцами не обнаружено. В противном случае возвращается значение, выдаваемое последней командой из соответствующего списка.

Оператор select

Оператор select позволяет организовать интерактивное взаимодействие с пользователем. Он имеет следующий формат:
select name [ in word; ] do list ; done
Вначале из шаблона word формируется список слов, соответствующих шаблону. Этот набор слов выводится в стандартный поток ошибок, причем каждое слово сопровождается порядковым номером. Если шаблон word пропущен, таким же образом выводятся позиционные параметры. После этого выдается стандартное приглашение PS3 и оболочка ожидает ввода строки на стандартном вводе. Если введенная строка содержит число, соответствующее одному из отображенных слов, то переменной name присваивается значение, равное этому слову. Если введена пустая строка, то номера и соответствующие слова выводятся заново. Если введено любое другое значение, переменной name присваивается нулевое значение. Введенная пользователем строка запоминается в переменой REPLY. Список команд list выполняется с выбранным значением переменной name.

Оператор for

Оператор for работает немного не так, как в обычных языках программирования. Вместо того, чтобы организовывать увеличение или уменьшение на единицу значения некоторой переменной при каждом проходе цикла, он при каждом проходе цикла присваивает переменной очередное значение из заданного списка слов. В целом конструкция выглядит примерно так:
for name in words do list done.
Правила построения списков команд (list) такие же, как и в операторе if.

Пример:

for a in 1 2 3 ; do
	touch foo_$a
done

В общем случае оператор for имеет формат:

for name [ in word; ] do list ; done
Вначале производится раскрытие слова word в соотвествии с правилами раскрытия выражений, приведенными выше. Затем переменной name поочередно присваиваются полученные значения и каждый раз выполняется список команд list. Если "in word" пропущено, то список команд list выполняется один раз для каждого позиционного параметра, который задан.

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

for a in $( seq 1 10 ) ; do
	cat file_$a
done

Эта команда выводит на экран содержимое 10-ти файлов: "file_1", ..., "file_10".

Операторы while и until

Оператор while работает подобно if, только выполнение операторов из списка list2 циклически продолжается до тех пор, пока верно условие и прерывается, если условие не верно. Конструкция выглядит следующим образом:

while list1 do list2 done.

Пример:

while [ -d mydirectory ] ; do
	ls -l mydirectory >> logfile
	echo -- SEPARATOR -- >> logfile
	sleep 60
done

Такая программа будет протоколировать содержание каталога "mydirectory" ежеминутно до тех пор, пока директория существует.

Оператор until аналогичен оператору while:

until list1 do list2 done.
Отличие заключается в том, что результат, возвращаемый при выполнении списка операторов list1, берется с отрицанием: list2 выполняется в том случае, если последняя команда в списке list1 возращает ненулевой статус выхода.

Функции

Синтаксис

Оболочка bash позволяет пользователю создавать собственные функции. Функции ведут себя и используются точно так же, как обычные команды оболочки, то есть мы можем сами создавать новые команды. Функции конструируются следующим образом:

function name () { list },
причем слово function не обязательно, name определяет имя функции, по которому к ней можно обращаться, а тело функции состоит из списка команд list, находящегося между { и }. Этот список команд выполняется каждый раз, когда имя name задано как имя вызываемой команды. Отметим, что функции могут задаваться рекурсивно, так что разрешено вызывать фунцию, которую мы задаем, внутри нее самой.

Функции выполняются в контексте текущей оболочки: новый процесс не запускается для интерпретации функции (в отличие от выполнения скриптов оболочки).

Аргументы

Когда функция вызывается на выполнение, аргументы функции становятся позиционными параметрами (positional parameters) на время выполнения функции. Они именуются как $n, где n - номер аргумента, к которому мы хотим получить доступ. Нумерация аргументов начинается с 1, так что $1 - это первый аргумент. Мы можем также получить все аргументы сразу с помощью $*, и число аргументов с помощью $#. Позиционный параметр 0 не изменяется.

Если в теле функции встречается встроенная команда return, выполнение функции прерывается и управление передается команде, стоящей после вызова функции. Когда выполнение функции завершается, позиционным параметрам и специальному параметру # возвращаются те значения, которые они имели до начала выполнения функции.

Локальные переменные (local)

Если мы хотим создать локальный параметр, можно использовать ключевое слово local. Синтаксис ее задания точно такой же, как и для обычных параметров, только определению предшествует ключевое слово local: local name=value.

Вот пример задания функции, реализующей упоминавшуюся выше команду seq:
seq()
{
    local I=$1;
    while [ $2 != $I ]; do
        {
            echo -n "$I ";
            I=$(( $I + 1 ))
        };
    done;
    echo $2
}

Обратите внимание на опцию "-n" оператора echo, она отменяет переход на новую строку. Хотя это и несущественно для тех целей, которые мы здесь имеем ввиду, это может оказаться полезным для использования функции в других целях.

fact

Еще один пример:

fact()
{
    if [ $1 = 0 ]; then
        echo 1;
    else
        {
            echo $(( $1 * $( fact $(( $1 - 1 )) ) ))
        };
    fi
}

Это функция факториала, пример рекурсивной функции. Обратите внимание на арифметическое расширение и подстановку команд.

5.9. Скрипты оболочки и команда source

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

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

. filename [arguments]
source filename [arguments]
Эта команда читает и выполняет команды из файла с именем filename в текущем окружении и возвращает статус, определяемый последней командой из файла filename. Если filename не содержит слэша, то пути, перечисленные в переменной PATH, используются для поиска файла с именем filename. Этот файл не обязан быть исполняемым. Если в каталогах, перечисленных в PATH, нужный файл не найден, его поиск производится в текущем каталоге.

Если заданы аргументы, на время выполнения скрипта они становятся позиционными параметрами. Если аргументов нет, позиционные параметры не изменяются. Значение (статус), возвращаемое командой source, совпадает со значением, возвращаемым последней командой, выполненной в скрипте. Если ни одна команда не выполнялась или файл filename не найден, то статус выхода равен 0.

sh

Вы всегда можете запустить новый экземпляр оболочки bash, дав команду bash или sh. При этом можно заставить новый экземпляр оболочки выполнить какой-то скрипт, если передать имя скрипта в виде аргумента команды bash. Так что для выполнения скрипта myscript надо дать команду "sh myscript".

Если Вы заглянете в какой-нибудь файл, задающий скрипт (таких файлов в системе очень много), Вы увидите, что первая строка в нем имеет вид: "#!/bin/sh". Это означает, что когда мы запускаем скрипт на выполнение как обычную команду, /bin/sh будет выполнять ее для нас. Можно заменить эту строку ссылкой на любую программу, которая будет читать файл и исполнять соответствующие команды. Например, скрипты на языке perl начинаются со строки вида "#!/bin/perl".

Отметим также, что символ # служит для выделения в скриптах комментариев. Все, что стоит в текущей строке после этого символа и до символа конца строки, оболочка будет считать комментариями и игнорировать (то есть оболочка не рассматривает этот текст как команды). Если хотите убедиться в действии этого символа, введите в командной строке любую команду, поставив перед ней символ #, например, "# ls", и Вы увидите, что команда игнорируется оболочкой.


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

В.А.Костромин
Последние изменения
в содержание файла внесены
5 февраля 2001 г.
TopList Aport Ranker