355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Уильям Шоттс » Командная строка Linux » Текст книги (страница 24)
Командная строка Linux
  • Текст добавлен: 12 апреля 2017, 12:30

Текст книги "Командная строка Linux"


Автор книги: Уильям Шоттс


Жанр:

   

ОС и Сети


сообщить о нарушении

Текущая страница: 24 (всего у книги 30 страниц)

26. Проектирование сверху вниз

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

Представьте, что нам нужно описать типичную повседневную задачу – сходить в магазин и купить продукты – для пришельца с Марса. Весь процесс можно разбить на следующую последовательность шагов:

1. Сесть в машину.

2. Доехать до магазина.

3. Припарковать машину.

4. Войти в магазин.

5. Купить продукты.

6. Вернуться в машину.

7. Доехать до дома.

8. Припарковать машину.

9. Войти в дом.

Однако инопланетянину с Марса почти наверняка потребуется больше деталей. Задачу «Припарковать машину» мы могли разбить на еще более мелкие шаги.

1. Найти место на парковке.

2. Поставить машину на это место.

3. Выключить двигатель.

4. Поставить на стояночный тормоз.

5. Выйти из машины.

6. Запереть машину.

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

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

В этой главе воспользуемся приемом проектирования сверху вниз для дальнейшей разработки сценария генератора отчетов.

Функции командной оболочки

В настоящий момент наш сценарий генерирует документ HTML, выполняя следующие шаги:

1. Открыть страницу.

2. Открыть заголовок страницы.

3. Установить название страницы.

4. Закрыть заголовок страницы.

5. Открыть тело страницы.

6. Вывести заголовок на странице.

7. Вывести текущее время.

8. Закрыть тело страницы.

9. Закрыть страницу.

На следующем этапе разработки мы добавим несколько задач между шагами 7 и 8:

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

• Дисковое пространство – информация об использовании дискового пространства на системных устройствах хранения.

• Объем домашних каталогов – объем дискового пространства, занятого каждым пользователем.

Если бы у нас были команды, решающие перечисленные задачи, мы бы просто добавили их в сценарий, воспользовавшись механизмом подстановки результатов команд:

#!/bin/bash

# Программа вывода страницы с информацией о системе

TITLE="System Information Report For $HOSTNAME"

CURRENT_TIME=$(date +"%x %r %Z")

TIME_STAMP="Generated $CURRENT_TIME, by $USER"

cat << _EOF_

        

                $TITLE

        

        

                $TITLE

                

$TIME_STAMP

                $(report_uptime)

                $(report_disk_space)

                $(report_home_space)

        

_EOF_

Создать такие команды можно двумя способами: написать три отдельных сценария и поместить их в каталог, входящий в список PATH, или встроить эти сценарии в программу в виде функций командной оболочки. Как уже отмечалось ранее, функции – это «мини-сценарии», находящиеся внутри другого сценария, которые работают как автономные программы. Функции имеют две синтаксические формы. Первая выглядит так:

function имя {

        команды

        return

}

где имя – это имя функции, а команды – последовательность команд внутри функции. Вторая форма выглядит так:

имя () {

        команды

        return

}

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

1     #!/bin/bash

2

3     # Демонстрация функций командной оболочки

4

5     function funct {

6             echo "Step 2"

7             return

8     }

9

10     # Здесь начинается основная программа

11

12     echo "Step 1"

13     funct

14     echo "Step 3"

Когда командная оболочка читает сценарий, она пропускает строки с 1-й по 11-ю, так как они содержат комментарии и определение функции. Выполнение начинается со строки 12 с командой echo. Строка 13 вызывает функцию funct, и командная оболочка выполняет функцию как любую другую команду. Управление передается в строку 6, и выполняется вторая команда echo. Следующей выполняется строка 7. Команда return в этой строке завершает выполнение функции и возвращает управление в строку, следующую за вызовом функции (строка 14). После этого выполняется заключительная команда echo. Обратите внимание: чтобы вызовы функций интерпретировались не как имена внешних программ, а действительно как вызовы функций, эти функции должны быть определены в сценарии до их вызова.

Добавим в наш сценарий минимальные определения функций:

#!/bin/bash

# Программа вывода страницы с информацией о системе

TITLE="System Information Report For $HOSTNAME"

CURRENT_TIME=$(date +"%x %r %Z")

TIME_STAMP="Generated $CURRENT_TIME, by $USER"

report_uptime () {

        return

}

report_disk_space () {

        return

}

report_home_space () {

        return

}

cat << _EOF_

        

                $TITLE

        

        

                $TITLE

                

$TIME_STAMP

                $(report_uptime)

                $(report_disk_space)

                $(report_home_space)

        

_EOF_

Имена функций подчиняются тем же правилам, что и имена переменных. Функция должна содержать хотя бы одну команду. Команда return (которая является необязательной) помогает удовлетворить это требование.

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

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

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

Следующий пример сценария демонстрирует, как определяются и используются локальные переменные:

#!/bin/bash

# local-vars: сценарий, демонстрирующий локальные переменные

foo=0   # глобальная переменная foo

funct_1 () {

        local foo         # переменная foo, локальная для funct_1

        foo=1

        echo "funct_1: foo = $foo"

}

funct_2 () {

        local foo         # переменная foo, локальная для funct_2

        foo=2

        echo "funct_2: foo = $foo"

}

echo "global:  foo = $foo"

funct_1

echo "global:  foo = $foo"

funct_2

echo "global:  foo = $foo"

Как видите, локальные переменные объявляются добавлением слова local перед именем переменной. В результате создается переменная, локальная по отношению к функции, в которой она определена. Когда выполнение выйдет за пределы функции, переменная перестанет существовать. Если запустить этот сценарий, он выведет следующее:

[me@linuxbox ~]$ local-vars

global:  foo = 0

funct_1: foo = 1

global:  foo = 0

funct_2: foo = 2

global:  foo = 0

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

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

Постоянное опробование сценария

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

[me@linuxbox ~]$ sys_info_page

        

                System Information Report For twin2

        

        

                System Information Report For linuxbox

                

Generated 03/19/2012 04:02:10 PM EDT, by me

        

Изменим функции, добавив в них сообщения для обратной связи:

report_uptime () {

        echo «Function report_uptime executed.»

        return

}

report_disk_space () {

        echo «Function report_disk_space executed.»

        return

}

report_home_space () {

        echo «Function report_home_space executed.»

        return

}

И запустим сценарий еще раз:

[me@linuxbox ~]$ sys_info_page

        

                System Information Report For linuxbox

        

        

                System Information Report For linuxbox

                

Generated 03/20/2012 05:17:26 AM EDT, by me

                Function report_uptime executed.

                Function report_disk_space executed.

                Function report_home_space executed.

        

Теперь можно с уверенностью сказать, что наши три функции выполняются как надо.

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

report_uptime () {

        cat <<– _EOF_

                

System Uptime

                

$(uptime)

                EOF_

        return

}

Она выглядит очень просто. Мы использовали встроенный документ для вывода заголовка раздела и результатов выполнения команды uptime, заключив их в теги

, чтобы сохранить формат вывода команды. Функция report_disk_space выглядит аналогично:

report_disk_space () {

        cat <<– _EOF_

                

Disk Space Utilization

                

$(df -h)

                _EOF_

        return

}

Она получает информацию о дисковом пространстве с помощью команды df -h. Наконец, определим функцию report_home_space:

report_home_space () {

        cat <<– _EOF_

                

Home Space Utilization

                

$(du -sh /home/*)

                _EOF_

        return

}

Для решения поставленной задачи мы использовали команду du с параметрами -sh. Однако это не полное решение задачи. Даже при том, что его можно использовать в некоторых системах (например, в Ubuntu), кое-где оно работать не будет. Причина в том, что во многих системах для домашних каталогов выбираются разрешения, не позволяющие читать их содержимое другим пользователям, что является вполне разумной мерой предосторожности. В этих системах функция report_home_space в том виде, в каком она написана здесь, будет работать, только если запустить сценарий с правами суперпользователя. Лучшее, что можно сделать в такой ситуации, – корректировать поведение сценария в соответствии с привилегиями пользователя, запустившего его. Мы займемся этим в главе 27.

функции командной оболочки в файле .BASHRC

Функции командной оболочки могут служить прекрасной заменой псевдонимам и в действительности считаются предпочтительным способом определения небольших команд для личного использования. Возможности псевдонимов весьма ограниченны в отношении использования некоторых видов команд и особенностей командной оболочки, тогда как функции позволяют все, что можно выразить в виде сценария. Например, если вам понравилась функция report_disk_space, созданная нами для нашего сценария, вы можете создать похожую функцию с именем ds в своем файле .bashrc:

ds () {

    echo "Disk Space Utilization For $HOSTNAME"

    df -h

}

Заключительное замечание

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

27. Управление потоком выполнения: ветвление при помощи if

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

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

x = 5

Если x = 5, тогда:

Сказать «x равно 5».

Иначе:

Сказать «x не равно 5».

Это – пример ветвления. Если условие «x = 5?» верно, выполняется строка: «Сказать ‘x равно 5’». Иначе выполняется строка: «Сказать ‘x не равно 5’».

Использование if

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

x=5

if [ $x = 5 ]; then

        echo "x equals 5."

else

        echo "x does not equal 5."

fi

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

[me@linuxbox ~]$ x=5

[me@linuxbox ~]$ if [ $x = 5 ]; then echo «equals 5»; else echo «does not equal 5»; fi

equals 5

[me@linuxbox ~]$ x=0

[me@linuxbox ~]$ if [ $x = 5 ]; then echo «equals 5»; else echo «does not equal 5»; fi

does not equal 5

В этом примере мы выполнили команду дважды. Первый раз со значением 5 в переменной x, что привело к выводу строки equals 5, и второй раз со значением 0 в переменной x, что привело к выводу строки not equal 5.

Инструкция if имеет следующий синтаксис:

if команды; then

        команды

[elif команды; then

        commands...]

[else

        команды]

fi

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

Код завершения

Команды (включая сценарии и функции, написанные нашими собственными руками) по завершении работы возвращают системе значение, которое называют кодом завершения (exit status). Это значение – целое число в диапазоне от 0 до 255 – сообщает об успешном или неуспешном завершении команды. По соглашениям значение 0 служит признаком успешного завершения, а любое другое – неуспешного. Командная оболочка поддерживает переменную, посредством которой можно определить код завершения. Например:

[me@linuxbox ~]$ ls -d /usr/bin

/usr/bin

[me@linuxbox ~]$ echo $?

0

[me@linuxbox ~]$ ls -d /bin/usr

ls: cannot access /bin/usr: No such file or directory

[me@linuxbox ~]$ echo $?

2

В этом примере мы дважды выполнили команду ls. В первый раз команда выполнилась благополучно. Если вывести значение переменной $?, можно увидеть, что оно равно 0. Во второй раз команда ls сообщила об ошибке, а переменная $? содержала значение 2, указывающее, что команда столкнулась с ошибкой. Одни команды используют разные коды завершения, чтобы сообщить о характере ошибки, тогда как другие, столкнувшись с любой ошибкой, просто возвращают значение 1. Страницы справочного руководства часто включают раздел с заголовком «Exit Status» («Коды завершения»), описывающий возвращаемые коды. Однако 0 всегда служит признаком успешного выполнения.

Командной оболочкой поддерживаются две чрезвычайно простые встроенные команды, которые просто завершаются с кодом 0 или 1. Команда true всегда завершается с признаком успеха, а команда false – всегда с признаком ошибки:

[me@linuxbox ~]$ true

[me@linuxbox ~]$ echo $?

0

[me@linuxbox ~]$ false

[me@linuxbox ~]$ echo $?

1

Эти команды можно использовать для исследования особенностей работы инструкции if. Инструкция if в действительности просто оценивает код завершения команды:

[me@linuxbox ~]$ if true; then echo «It's true.»; fi

It's true.

[me@linuxbox ~]$ if false; then echo «It's true.»; fi

[me@linuxbox ~]$

Команда echo "It's true." выполняется, только если команда, следующая за if, завершается успешно, и не выполняется, если команда, следующая за if, завершается с признаком ошибки. Если за if следует список команд, успешность выполнения всего списка определяется по последней команде:

[me@linuxbox ~]$ if false; true; then echo «It's true.»; fi

It's true.

[me@linuxbox ~]$ if true; false; then echo «It's true.»; fi

[me@linuxbox ~]$

Команда test

Вне всяких сомнений, чаще всего с инструкцией if используется команда test. Команда test может выполнять различные проверки и сравнения. Она имеет две эквивалентные формы:

test выражение

и более популярную

[ выражение ]

где выражение возвращает истинное (true) или ложное (false) значение. Команда test возвращает код завершения 0, если выражение истинно, и код завершения 1, если выражение ложно.

Выражения для проверки файлов

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

Таблица 27.1. Выражения для проверки файлов

Выражение

Истинно, если...

файл1 -ef файл2

файл1 и файл2 имеют одно и то же число индексного узла (inode; то есть два имени принадлежат жестким ссылкам, ссылающимся на один и тот же файл)

файл1 -nt файл2

файл1 новее файла файл2

файл1 -ot файл2

файл1 старше файла файл2

–b файл

файл существует и является специальным файлом блочного устройства

–с файл

файл существует и является специальным файлом символьного устройства

–d файл

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

–e файл

файл существует

–f файл

файл существует и является обычным файлом

–g файл

файл существует и имеет атрибут set-group-ID (бит setgid)

–G файл

файл существует и принадлежит действующей группе

–k файл

файл существует и имеет атрибут «sticky bit»

–L файл

файл существует и является символической ссылкой

–O файл

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

–p файл

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

–r файл

файл существует и доступен для чтения (имеет разрешение на чтение для действующего пользователя)

–s файл

файл существует и имеет размер больше нуля

–S файл

файл существует и является сетевым сокетом

–t дескриптор_файла

дескриптор_файла представляет файл, подключенный к терминалу. Это выражение можно использовать для проверки стандартных потоков ввода/вывода/ошибок

–u файл

файл существует и имеет атрибут setuid

–w файл

файл существует и доступен для записи (имеет разрешение на запись для действующего пользователя)

–x файл

файл существует и доступен для выполнения (имеет разрешение на выполнение для действующего пользователя)

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

#!/bin/bash

# test-file: проверка файла

FILE=~/.bashrc

if [ -e "$FILE" ]; then

        if [ -f "$FILE" ]; then

                echo "$FILE is a regular file."

        fi

        if [ -d "$FILE" ]; then

                echo "$FILE is a directory."

        fi

        if [ -r "$FILE" ]; then

                echo "$FILE is readable."

        fi

        if [ -w "$FILE" ]; then

                echo "$FILE is writable."

        fi

        if [ -x "$FILE" ]; then

                echo "$FILE is executable/searchable."

        fi

else

        echo "$FILE does not exist"

        exit 1

fi

exit

Сценарий проверяет файл, имя которого присвоено константе FILE, и выводит результат. Этот сценарий имеет две интересные особенности, на которые следует обратить внимание. Во-первых, отметьте, что параметр $FILE внутри выражений заключен в кавычки. Это не является обязательным требованием, но защищает от случаев, когда параметр пуст. Если механизм подстановки заменит $FILE пустым значением, это приведет к ошибке (операторы в этом случае будут интерпретироваться как непустые строки, а не как операторы). Использование кавычек гарантирует, что за оператором всегда будет следовать строка, даже если она пустая. Во-вторых, обратите внимание на команду exit (в конце сценария). Команда exit принимает единственный необязательный аргумент, определяющий код возврата сценария. В отсутствие аргумента exit вернет значение по умолчанию 0. Такое использование exit позволит сценарию сообщить об ошибке, если в $FILE содержится имя несуществующего файла. Команда exit в самом конце сценария добавлена исключительно для формальности. Когда командная оболочка достигает конца сценария (то есть конца файла), она в любом случае завершает выполнение сценария с кодом завершения 0.

Аналогично, функции могут возвращать свой код завершения, передавая целочисленный аргумент команде return. Чтобы преобразовать сценарий, приведенный выше, в функцию для использования в больших программах, нужно заменить команды exit инструкциями return:

test_file () {

        # test-file: проверка файла

        FILE=~/.bashrc

        if [ -e "$FILE" ]; then

                if [ -f "$FILE" ]; then

                        echo "$FILE is a regular file."

                fi

                if [ -d "$FILE" ]; then

                        echo "$FILE is a directory."

                fi

                if [ -r "$FILE" ]; then

                        echo "$FILE is readable."

                fi

                if [ -w "$FILE" ]; then

                        echo "$FILE is writable."

                fi

                if [ -x "$FILE" ]; then

                        echo "$FILE is executable/searchable."

                fi

        else

                echo "$FILE does not exist"

                return 1

        fi

}

Выражения для проверки строк

В табл. 27.2 перечислены выражения, используемые для проверки строк.

Таблица 27.2. Выражения для проверки строк

Выражение

Истинно, если...

строка

Cтрока не пустая

–n строка

Длина строки больше нуля

–z строка

Длина строки равна нулю

строка1 = строка2

строка1 == строка2

строка1 и строка2 равны. Допускается использовать один или два знака «равно», но предпочтительнее два

строка1 != строка2

строка1 и строка2 не равны

строка1 > строка2

строка1 больше, чем строка2, в смысле алфавитной сортировки

строка1 < строка2

строка1 меньше, чем строка2, в смысле алфавитной сортировки

ВНИМАНИЕ

При использовании с командой test операторы > и < необходимо заключать в кавычки (или экранировать символом обратного слеша). Если этого не сделать, они будут интерпретироваться командной оболочкой как операторы перенаправления, что может привести к плачевным результатам. Обратите также внимание: в документации к командной оболочке bash утверждается, что порядок сортировки соответствует порядку алфавитной сортировки, определяемому текущими региональными настройками, но в действительности это не так. В версиях bash, вплоть до 4.0, используется порядок сортировки ASCII (POSIX).

Следующий сценарий демонстрирует применение выражений для проверки строк:

#!/bin/bash

# test-string: проверка значения строки

ANSWER=maybe

if [ -z "$ANSWER" ]; then

        echo "There is no answer." >&2

        exit 1

fi

if [ "$ANSWER" == "yes" ]; then

        echo "The answer is YES."

elif [ "$ANSWER" == "no" ]; then

        echo "The answer is NO."

elif [ "$ANSWER" == "maybe" ]; then

        echo "The answer is MAYBE."

else

        echo "The answer is UNKNOWN."

fi

В этом сценарии определяется константа ANSWER. Сначала сценарий проверяет, не является ли строка пустой. Если строка пустая, сценарий завершается с кодом 1. Обратите внимание на оператор перенаправления в команде echo. Он перенаправляет сообщение об ошибке «There is no answer» («Нет ответа») в стандартный вывод ошибок как «наиболее подходящий» для сообщений об ошибках. Если строка не пустая, сценарий сравнивает ее значение со строками «yes», «no» или «maybe». Проверки выполняются с использованием инструкции elif, которая является краткой формой записи для else if. Инструкция elif позволяет конструировать более сложные логические проверки.

Выражения для проверки целых чисел

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

Таблица 27.3. Выражения для проверки целых чисел

Выражение

Истинно, если...

число1 -eq число2

число1 и число2 равны

число1 -ne число2

число1 и число2 не равны

число1 -le число2

число1 меньше или равно числу2

число1 -lt число2

число1 меньше, чем число2

число1 -ge число2

число1 больше или равно числу2

число1 -gt число2

число1 больше, чем число2

Следующий сценарий демонстрирует их применение:

#!/bin/bash

# test-integer: проверка целочисленного значения.

INT=-5

if [ -z "$INT" ]; then

        echo "INT is empty." >&2

        exit 1

fi

if [ $INT -eq 0 ]; then

        echo "INT is zero."

else

        if [ $INT -lt 0 ]; then

                echo "INT is negative."

        else

                echo "INT is positive."

        fi

        if [ $((INT % 2)) -eq 0 ]; then

                echo "INT is even."

        else

                echo "INT is odd."

        fi

fi

Обратите внимание на то, как сценарий определяет четность (even) или нечетность (odd) целого числа. Он возвращает остаток от деления числа на 2, по значению которого можно судить о четности или нечетности числа.

Более современная версия команды test

Последние версии bash реализуют составную команду, которая действует как улучшенная замена для команды test. Она имеет следующий синтаксис:

[[ выражение ]]

где выражение возвращает истинное (true) или ложное (false) значение. Команда [[ ]] очень похожа на команду test (она поддерживает те же выражения), но добавляет новое выражение для проверки строк:

строка1 =~ регулярное_выражение

возвращающее истинное значение, если строка1 соответствует расширенному регулярному выражению. Это открывает широкие перспективы для решения таких задач, как проверка корректности данных. Предыдущий сценарий, демонстрирующий применение выражений проверки целых чисел, может завершиться с ошибкой, если константе INT присвоить любое значение, не являющееся целым числом. Для надежности сценарию необходима возможность убедиться, что константа действительно содержит целое число. Используя [[ ]] с оператором проверки строки =~, мы усовершенствуем его, как показано ниже:

#!/bin/bash

# test-integer2: проверка целочисленного значения.

INT=-5

if [[ «$INT» =~ ^-?[0-9]+$ ]]; then

        if [ $INT -eq 0 ]; then

                echo "INT is zero."

        else

                if [ $INT -lt 0 ]; then

                        echo "INT is negative."

                else

                echo "INT is positive."

                fi

                if [ $((INT % 2)) -eq 0 ]; then

                        echo "INT is even."

                else

                        echo "INT is odd."

                fi

        fi

else

        echo «INT is not an integer.» >&2

        exit 1

fi

Применив регулярное выражение, мы смогли ограничить круг проверяемых значений в константе INT только строками, начинающимися с необязательного знака «минус», за которым следует одна или несколько цифр. Это выражение также устраняет вероятность появления пустых значений.

Еще одна дополнительная особенность [[ ]]: оператор == поддерживает сопоставление с шаблоном по аналогии с механизмом подстановки путей. Например:

[me@linuxbox ~]$ FILE=foo.bar

[me@linuxbox ~]$ if [[ $FILE == foo.* ]]; then

> echo «$FILE matches pattern 'foo.*'»

> fi

foo.bar matches pattern 'foo.*'

Она превращает [[ ]] в удобный инструмент проверки имен файлов и путей.

(( )) – для проверки целых чисел

В дополнение к составной команде [[ ]] bash поддерживает также составную команду (( )), которую удобно использовать для работы с целыми числами. Она поддерживает полное множество арифметических операторов, о которых подробно рассказывается в главе 34.

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

[me@linuxbox ~]$ if ((1)); then echo «It is true.»; fi

It is true.

[me@linuxbox ~]$ if ((0)); then echo «It is true.»; fi

[me@linuxbox ~]$

Применив (( )), можно немного упростить сценарий test-integer2, как показано ниже:

#!/bin/bash

# test-integer2a: проверка целочисленного значения.

INT=-5

if [[ "$INT" =~ ^-?[0-9]+$ ]]; then

        if ((INT == 0)); then

                echo "INT is zero."

        else

                if ((INT < 0)); then

                        echo "INT is negative."

                else

                        echo "INT is positive."

                fi

                if (( ((INT % 2)) == 0)); then

                        echo "INT is even."

                else

                        echo "INT is odd."

                fi

        fi

else

        echo "INT is not an integer." >&2

        exit 1

fi

Обратите внимание, что здесь мы использовали знак «меньше», а равенство проверяется с помощью оператора ==. Такой синтаксис выглядит более естественным при работе с целыми числами. Отметьте также, что составная команда (( )) является частью синтаксиса командной оболочки, а не обычной командой, может применяться только к целым числам, распознает переменные по именам и не требует выполнять подстановку.

Объединение выражений

Для более сложных вычислений существует возможность объединения выражений. Объединяются выражения с помощью логических операторов. Мы уже встречались с ними в главе 17, когда изучали команду find. Всего команды test и [[ ]] поддерживают три логические операции. Это И (AND), ИЛИ (OR) и НЕ (NOT). Для представления этих операций test и [[ ]] используют разные операторы, как показано в табл. 27.4.


    Ваша оценка произведения:

Популярные книги за неделю