kallypigos: (hybrid)
[personal profile] kallypigos

Комбинирование команд

Перейдем теперь к главной гордости интерпретаторов Unix — комбинированию команд. Вначале рассмотрим его простейшие варианты.

Как уже говорилось, из одной командной строки в bash можно запустить несколько программ последовательно, записав соответствующие команды через точку с запятой (?;?), или параллельно, разделив их амперсандами (?&?). Имеется, конечно, и так называемая «труба» (pipe), перенаправляющая стандартный выход одной программы на стандартный вход другой. Из Unix она перекочевала и в DOS, но там, впрочем, реализована «халтурно»: даже при использовании интерпретатора DOS 7.x из командной оболочки Windows 9X, где ядро ОС имеет все необходимые средства, первая команда полностью отрабатывает перед началом выполнения второй, а данные передаются через временный файл.

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

[khim@localhost khim]$ echo $PATH
/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/usr/local/bin:/opt/bin:/home/khim/bin

[khim@localhost khim]$ ( PATH=/bin ; echo $PATH )
/bin

[khim@localhost khim]$ echo $PATH
/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/usr/local/bin:/opt/bin:/home/khim/bin

[khim@localhost khim]$ ( PATH=/bin ; echo $PATH ;)
/bin

[khim@localhost khim]$ echo $PATH
/bin
[khim@localhost khim]$

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

Команды можно скомбинировать и таким довольно неожиданным способом, как сделав одну параметром (либо иной частью) другой. Команда, заключенная в скобки, перед которыми ставится символ ?$?, или в обратные апострофы, выполняется, а результат работы (тот, что был бы выдан в стандартный выводной поток) подставляется на ее место в командную строку.

[khim@localhost khim]$ export LC_ALL=english
[khim@localhost khim]$ date
Fri Aug 18 19:51:24 MSD 2000

[khim@localhost khim]$ LC_ALL="`( . /etc/sysconfig/i18n ; echo \"$LOCALE\";)`" date
Птн Авг 18 19:55:17 MSD 2000

[khim@localhost khim]$ echo $LOCALE

[khim@localhost khim]$

Рис. 2. Можно вставить одну команду в другую

Взгляните на рис. 2. Здесь мы сначала заносим в переменную окружения LC_ALL, определяющую параметры страны, значение english, а затем выводим дату на языке страны, указанной в файле /etc/sysconfig/i18n (там задаются стандартные настройки системы). Чтобы это сделать, мы записываем в LC_ALL новое значение. Какое же?

Давайте разберемся по порядку. Внутри кавычек (они, как обычно, поставлены на тот случай, если в значении, присваиваемом переменной LC_ALL, окажется пробел; в действительности это невозможно, но, как говорится, кашу маслом не испортишь) мы видим в обратных апострофах команду с именем ?.? и одним параметром /etc/sysconfig/i18n. Что она делает? Выполняет строки файла, указанного в качестве параметра, так, как если бы они были введены в этом месте в «натуральном виде». В частности, если в файле определяются переменные (а файл /etc/sysconfig/i18n содержит только определение переменных и комментарии), то они становятся доступны. Одну из них — LOCALE — мы выводим следующей командой, и она подставляется в командную строку в качестве значения, присваемого переменной LC_ALL.

Вместо обратных апострофов можно было использовать и конструкцию $(...). А очень похожая на нее конструкция $((...)) позволяет подставить в командную строку значение арифметического выражения (см. рис. 3; команда date с параметром +%s выдает текущую дату и время как число секунд, прошедших с 1 января 1970 г. до настоящего момента).

[khim@localhost khim]$ echo $((2+2))
4

[khim@localhost khim]$ echo "Прошло "((`date +%s` часов с \"начала времен\" Unix'а."
[khim@localhost khim]$

Рис. 3. Конструкция $((...)) — встроенный калькулятор bash

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

Коды возврата и проверка условий

По общепринятому соглашению код возврата равен 0, если работа программы завершилась успешно, и другому числу в противном случае (рис. 4). Код возврата последней выполнившейся в данном сеансе команды содержится в переменной $?.

[khim@localhost khim]$ bzip2 /bin/bash
bzip2: Can't create output file /bin/bash.bz2: Доступ запрещен.

[khim@localhost khim]$ echo $?
1

[khim@localhost khim]$ bzip2 /bin/bash > /dev/null
[khim@localhost khim]$ echo $?
0

[khim@localhost khim]$

Рис. 4. Программа bzip2 в соответствии с общепринятым соглашением возвращает 0 при успешном завершении операции и 1 при неудачном

С этими кодами возможны разнообразные действия, из которых важнейшими являются логические операции: отрицание (?!?), конъюнкция (?&&?) и дизъюнкция (?||?), как это показано на рис. 5. (Не путайте конъюнкцию с параллельным запуском, а дизъюнкцию — с «трубой»!)

Заметьте, если результата выполнения первой команды достаточно для определения кода возврата всей конструкции, то вторая команда не вызывается: это видно из сравнения двух примеров использования ?||? на рис. 5.

[khim@localhost khim]$ ! bzip2 /bin/bash
bzip2: Can't create output file /bin/bash.bz2: Доступ запрещен.

[khim@localhost khim]$ echo $?
0

[khim@localhost khim]$ ! bzip2 -c /bin/bash > /dev/null
[khim@localhost khim]$ echo $?
1
[khim@localhost khim]$ bzip2 -c /bin/bash > /dev/null || bzip2 /bin/bash

[khim@localhost khim]$ echo $?
0

[khim@localhost
khim]$ bzip2 /bin/bash || bzip2 -c /bin/bash > /dev/null
bzip2: Can't create output file /bin/bash.bz2: Доступ запрещен.
[khim@localhost khim]$ echo $?
0

[khim@localhost khim]$ bzip2 /bin/bash && bzip2 -c /bin/bash > /dev/null
bzip2: Can't create output file /bin/bash.bz2: Доступ запрещен.
[khim@localhost khim]$ echo $?
1

[khim@localhost khim]$

Рис. 5. С кодами возврата возможны логические операции

Любое арифметическое выражение также можно записать как команду и получить для него код возврата (рис. 6). Обратите внимание на два последних примера. Не кажется ли вам, что тут замешана какая-то мистика: 0 дает в результате 1, а 2 — 0? Дело в том, что внутри арифметических операторов действуют соглашения языка Си, т. е. истиной является любое число, кроме нуля, а ложью — нуль, вовне же поступает обычный код возврата, для которого все наоборот.

[khim@localhost khim]$ ((57<2000)) ; echo $?
1
57 не больше 2000
[khim@localhost khim]$ x=3
[khim@localhost khim]$ y=8
[khim@localhost khim]$ ((x**2>=y+1)) ; echo $?
0
x^2 >= y+1 ; $ не обязателен
[khim@localhost khim]$ ((x++,y=0))
[khim@localhost khim]$ echo "x == $x ; y == $y"
x == 4 ; y == 8 переменные можно изменить -- поддерживаются большинство операторов языка Си
[khim@localhost khim]$ ((1-1)) ; echo $?
0
2 как результат арифметического выражения -- истина, т. е. 0
[khim@localhost khim]$ ((1-1)) ; echo $?
1
0 как результат арифметического выражения -- ложь, т. е. 1
[khim@localhost khim]$
Рис. 6. Арифметические выражения могут вырабатывать код возврата

Проверки условий в bash... нет. Вернее, есть, но не в том виде, какой привычен нам по опыту работы в DOS. Ею занимаются специальные команды (в основном внутренние), каждая из которых проверяет определенный набор условий. Все они возвращают 0, если условие выполнено, и 1 в противном случае.

[khim@localhost khim]$ [ -e /bin ] ; echo $?
0
← /bin -- существует
[khim@localhost khim]$ [ -e /bin/sh ] ; echo $?
0
← /bin/sh -- существует
[khim@localhost khim]$ [ -f /bin ] ; echo $?
1
← /bin -- НЕ [обычный] файл
[khim@localhost khim]$ [ -f /bin/sh ] ; echo $?
0
← /bin/sh -- нормальный файл (на самом деле это символическая ссылка на обычный файл; [ эти случаи не различает)
[khim@localhost khim]$ [ -L /bin/sh ] ; echo $?
0
/bin/sh -- символическая ссылка
[khim@localhost khim]$ [ -L /bin/bash ] ; echo $?
1
/bin/bash -- не символическая ссылка
[khim@localhost khim]$ [ "abc" == "123" ] ; echo $?
1
/bin/bash -- abc не равно 123
[khim@localhost khim]$ [ "abc" ">" "xyz" ] ; echo $?
1
/bin/bash -- abc не больше xyz
[khim@localhost khim]$ [ "57" == "2000" ] ; echo $?
1
/bin/bash -- СТРОКА 57 меньше, чем СТРОКА 2000
[khim@localhost khim]$ [ "abc" == "123" ] ; echo $?
1
/bin/bash -- ЧИСЛО 57 меньше, чем ЧИСЛО 2000
[khim@localhost khim]$
Рис. 7. Команда [

Самыми первыми появились команды test и [, различающиеся только тем, что [ для симметрии требует последним параметром символа ]. Эти команды предназначены в основном для проверки разнообразных условий, связанных со свойствами файлов. Сравнивать с их помощью строки тоже можно, хотя и не очень удобно (рис. 7). Лучше пользоваться более мощной командой [[: она допускает сравнение с шаблоном и предусматривает более мнемоническую запись конъюнкции и дизъюнкции (рис. 8).

[khim@localhost khim]$ [[ abc == *b* ]] ; echo $?
0
← abc подходит под шаблон *b*
[khim@localhost khim]$ [[ *b* == abc ]] ; echo $?
1
← а шаблон должен быть слева
[khim@localhost khim]$ [[ abc == *x* || abc == *c* ]] ; echo $?
0
← дизъюнкция
[khim@localhost khim]$ [[abc == *x* && abc == *c* ]] ; echo $?
0
← конъюнкция
[khim@localhost khim]$ [[ -f /bin/sh ]] ; echo $?
0
← проверки файлов тоже работают
[khim@localhost khim]$ [[ 57 .lt. 2000 ]] ; echo $?
0
← как и числовые проверки
[khim@localhost khim]$

Рис. 8. Команда [[

Составные операторы

Теперь, наконец, можно перейти и к составным операторам. Их довольно много: условный оператор (см. листинг 1), оператор выбора (см. листинг 2), несколько операторов цикла: while (см. листинг 3), until, отличающийся от while только отрицанием условия, два цикла for (см. листинг 4 и листинг 5) и, разумеется, функции: они используются в основном в сложных скриптах, поэтому пример в листинге 6 — совсем игрушечный. Обратите, однако, внимание на конструкцию $??, позволяющую задавать спецсимволы тем же способом, что в языке Си: — табуляция, — конец строки и т. д.

Параметры и массивы

В bash, как и в командном интерпертаторе DOS, поддерживаются позиционные переменные, соответствующие параметрам скрипта, — $1, $2,.. В DOS они называются %1, %2 и т. д., и таким способом обозначаются параметры только до девятого, а если их больше, до следующих приходится добираться с помощью команды shift. Эта команда в bash тоже есть (как вы, наверное, догадались, она пришла в DOS из Unix), но кроме того, к любому параметру можно обратиться и непосредственно. Хотя в силу исторических причин интерпретатор воспримет запись $10 как переменную $1, за которой следует 0, форма ${10}, ${11} и т. д. будет понята правильно. А переменная $# содержит общее число параметров. Кстати, если нужно обратиться к последнему параметру, это удобно сделать в форме ${!#} (см. врезку «Извлечение значений переменных» в первой части статьи).

Для передачи всех параметров в другую программу в bash предусмотрены две специальные переменные: $* и $@. Переменная $* содержит список параметров; разделителем служит первый из символов, хранящихся в переменной окружения IFS (это список разделителей слов, используемый в основном командой read). А вот $@ магическим образом раскрывается в $# параметров (если используются кавычки, то результат бывает забавен — см. рис. 9). Так как перебор параметров скрипта — весьма частая операция, то in ?$@? в цикле for можно опустить, как это сделано в листинге 6.

[khim@localhost khim]$ ( IFS=zyz ; echo "$+" )
123
[khim@localhost khim]$ for i in "Strange $@ result"
do echo "$i"
done
Strange 1
2
3 result

[khim@localhost khim]$

Рис. 9. Переменные $* и $@

Ввод—вывод

Работа с файлами в bash основана на потоковом вводе-выводе. Каждый поток представлен в программе уникальным целым числом — дескриптором, который передается командам чтения и записи. (Точно так же обстоит дело и в DOS.) Для чтения чаще всего применяется команда read, а для записи — команда echo; с обеими мы уже много раз встречались в примерах.

Стандартному вводному потоку соответствуют файл /dev/stdin и дескриптор 0, стандартному выводному — файл /dev/stdout и дескриптор 1. Сообщения об ошибках направляются в файл /dev/stderr, открытый с дескриптором 2, а дескрипторы, начиная с третьего, используются по усмотрению программиста.

Операции перенаправления, из которых пользователям DOS знакомы >, >> и <, в bash работают не только со стандартными вводным и выводным потоками, но и с любыми иными. Например, чтобы открыть файл для чтения с дескриптором n, следует написать n<имя_файла, а чтобы открыть его для записи или для дописывания в конец (append) — соответственно n>имя_файла или n>>имя_файла. Дескриптор стандартного вводного потока в первом случае, а также стандартного выводного во втором и в третьем разрешается опустить, и тогда конструкции начинают выглядеть и работать так же, как в DOS. Запись &>имя_файла означает перенаправление в заданный файл дескрипторов 1 и 2 (т. е. стандартного ввода и сообщений об ошибках).

Запись &n соответствует файлу, открытому с дескриптором n (еще один способ обозначить такой файл — /dev/fd/n), поэтому написав, скажем, m>&n, мы направим поток m в тот же файл, что и поток n. Написав n<&-, мы закроем файл с дескриптором n.

В bash есть и другие операции перенаправления. Конструкция n<>имя_файла позволит открыть файл и для чтения, и для записи. А конструкция <<разделитель, используемая только в скриптах, перенаправляет в стандартный вводной поток следующие за ней строки файла скрипта вплоть до той, которая состоит из заданного разделителя. Ее вариант <<-разделитель дополнительно удаляет из всех перенаправленных строк начальные символы табуляции.

Точно так же, как присваивание значений переменным, перенаправление ввода-вывода может распространяться и на одну команду, и на весь интерпретатор. Правда, синтаксис во втором случае иной, чем при присваивании: перед операцией перенаправления необходимо поставить команду exec (см. листинг 8).

О bash можно рассказывать еще и еще, но, как гласит пословица, лучше один раз увидеть, чем семь раз услышать. Попробовав поработать в командной строке и написать пару-тройку несложных скриптов, вы наверняка поймете, за что опытные пользователи Linux (и вообще Unix-систем) так любят командные интерпретаторы. Надеюсь, мне удалось вдохновить вас на подобный эксперимент.
This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

Profile

kallypigos: (Default)
kallypigos

May 2017

S M T W T F S
 123456
78910111213
14151617181920
21222324 2526 27
28293031   

Style Credit

Expand Cut Tags

No cut tags
Page generated Jul. 24th, 2025 02:41 am
Powered by Dreamwidth Studios