Red Hat System Administration II 8.2

Более эффективное выполнение команд с помощью циклов

Задачи

После завершения этого раздела вы сможете:

  • проходить по спискам с помощью циклов for;

  • применять коды завершения в командах и сценариях;

  • выполнять тесты с помощью операторов;

  • создавать условные конструкции с помощью инструкций if.

Использование циклов для итерации команд

Системные администраторы ежедневно сталкиваются с повторяющимися задачами. Повторяющейся задачей может быть многократное выполнение действия над одним объектом, например проверка процесса каждую минуту в течение 10 минут, чтобы узнать, был ли он завершен. Это также может быть разовое выполнение действия над несколькими объектами, например резервное копирование каждой базы данных в системе. Цикл for — одна из многочисленных циклических конструкций в командной оболочке Bash, которую можно использовать для итерации задач.

Обработка элементов из командной строки

Конструкция цикла for в командной оболочке Bash имеет следующий синтаксис:

for VARIABLE in LIST; do
COMMAND VARIABLE
done

Цикл по порядку обрабатывает строки в LIST и завершается после обработки последней строки списка. Каждая строка в списке временно сохраняется как значение VARIABLE, пока цикл for выполняет блок команд, входящих в его конструкцию. Имя переменной может быть любым. Обычно значение переменной указывается в командах блока цикла.

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

[user@host ~]$ for HOST in host1 host2 host3; do echo $HOST; done
host1
host2
host3
[user@host ~]$ for HOST in host{1,2,3}; do echo $HOST; done
host1
host2
host3
[user@host ~]$ for HOST in host{1..3}; do echo $HOST; done
host1
host2
host3
[user@host ~]$ for FILE in file*; do ls $FILE; done
filea
fileb
filec
[user@host ~]$ for FILE in file{a..c}; do ls $FILE; done
filea
fileb
filec
[user@host ~]$ for PACKAGE in $(rpm -qa | grep kernel); \
do echo "$PACKAGE was installed on \ 
$(date -d @$(rpm -q --qf "%{INSTALLTIME}\n" $PACKAGE))"; done
abrt-addon-kerneloops-2.1.11-12.el7.x86_64 was installed on Tue Apr 22 00:09:07 EDT 2014
kernel-3.10.0-121.el7.x86_64 was installed on Thu Apr 10 15:27:52 EDT 2014
kernel-tools-3.10.0-121.el7.x86_64 was installed on Thu Apr 10 15:28:01 EDT 2014
kernel-tools-libs-3.10.0-121.el7.x86_64 was installed on Thu Apr 10 15:26:22 EDT 2014
[user@host ~]$ for EVEN in $(seq 2 2 10); do echo "$EVEN"; done
2
4
6
8
10

Использование кодов завершения в сценарии

Обработав все свое содержимое, сценарий выходит из процесса, который его вызвал. Однако могут быть случаи, когда нужно завершить сценарий в ходе его выполнения, например при обнаружении ошибки. Для этого можно использовать в сценарии команду exit. Когда сценарий доходит до команды exit, он немедленно завершается, при этом оставшаяся часть сценария не обрабатывается.

Команду exit можно выполнить с необязательным целочисленным аргументом в диапазоне от 0 до 255, представляющим код завершения. Код завершения — это код, который возвращается после завершения процесса. Код завершения 0 означает отсутствие ошибки. Все другие значения кода завершения, отличные от нуля, означают ошибку. Вы можете использовать разные ненулевые значения для разграничения разных типов обнаруженных ошибок. Этот код завершения передается обратно в родительский процесс, который сохраняет его в переменной ?. Узнать код можно с помощью опции $?, как показано в следующих примерах.

[user@host bin]$ cat hello
#!/bin/bash
echo "Hello, world"
exit 0

[user@host bin]$ ./hello
Hello, world

[user@host bin]$ echo $?
0

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

Тестирование входных данных сценария

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

Как и все команды, команда test после выполнения выдает код завершения, который сохраняется как значение $?. Чтобы увидеть результат теста, отобразите значение $? сразу после выполнения команды test. Код завершения 0 означает, что тест пройден успешно, а значения, отличные от нуля, означают, что тест не пройден.

Тесты выполняются с использованием различных операторов. Операторы позволяют сравнивать числа (больше, больше или равно, меньше, меньше или равно, равно). С их помощью можно проверить, являются ли строки текста одинаковыми. Операторы также можно использовать для проверки наличия значений в переменных.

Примечание

В сценариях командной оболочки используется множество различных операторов, помимо рассматриваемых здесь операторов сравнения. На man-странице для test(1) приведены важные операторы условных выражений с описаниями. На man-странице bash(1) также объясняется использование и вычисление операторов, но начинающим может быть сложно в этом разобраться. Мы рекомендуем учащимся и далее развивать свои навыки по написанию расширенных сценариев командной оболочки с помощью книг и курсов, посвященных программированию в командной оболочке.

В следующих примерах демонстрируется использование команды test с операторами Bash для сравнения чисел.

[user@host ~]$ test 1 -gt 0 ; echo $?
0
[user@host ~]$ test 0 -gt 1 ; echo $?
1

Тесты можно выполнить с использованием синтаксиса команды тестирования Bash: [ <TESTEXPRESSION> ]. Их также можно провести с помощью более нового расширенного синтаксиса команды тестирования Bash ― [[ <TESTEXPRESSION> ]], который доступен с версии Bash 2.02 и предоставляет такие функции, как подстановка имен файлов и регулярных выражений.

В следующих примерах демонстрируется использование синтаксиса команды тестирования Bash и операторов сравнения чисел Bash.

[user@host ~]$ [ 1 -eq 1 ]; echo $?
0
[user@host ~]$ [ 1 -ne 1 ]; echo $?
1
[user@host ~]$ [ 8 -gt 2 ]; echo $?
0
[user@host ~]$ [ 2 -ge 2 ]; echo $?
0
[user@host ~]$ [ 2 -lt 2 ]; echo $?
1
[user@host ~]$ [ 1 -lt 2 ]; echo $?
0

В следующих примерах демонстрируется использование операторов сравнения строк Bash.

[user@host ~]$ [ abc = abc ]; echo $?
0
[user@host ~]$ [ abc == def ]; echo $?
1
[user@host ~]$ [ abc != def ]; echo $?
0

В следующих примерах демонстрируется использование унарных операторов сравнения строк Bash.

[user@host ~]$ STRING=''; [ -z "$STRING" ]; echo $?
0
[user@host ~]$ STRING='abc'; [ -n "$STRING" ]; echo $?
0

Примечание

Пробелы внутри скобок обязательны, поскольку они разделяют слова и элементы в выражении теста. Оболочка разделяет все командные строки на слова и операторы, распознавая пробелы и другие метасимволы с помощью встроенных правил разбора. Полное описание этой расширенной концепции см. на man-странице getopt(3). Символ левой квадратной скобки ([) ― это встроенный псевдоним команды test. Слова командной оболочки (команды, подкоманды, опции, аргументы и другие элементы токенов) всегда разделяются пробелами.

Условные конструкции

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

Использование конструкции if/then

Самая простая условная конструкция в Bash — это конструкция if/then, которая имеет следующий синтаксис:

if <CONDITION>; then
      <STATEMENT>
      ...
      <STATEMENT>
fi

В рамках этой конструкции выполняется одно или несколько действий, если соблюдено определенное условие. Если условие не соблюдено, действие не выполняется. Описанные ранее числовые, строковые и файловые тесты часто применяются для проверки условий в инструкциях if/then. Инструкция fi в конце закрывает конструкцию if/then. В следующем отрывке кода демонстрируется использование конструкции if/then для запуска службы psacct, если она неактивна.

[user@host ~]$ systemctl is-active psacct > /dev/null 2>&1
[user@host ~]$ if  [ $? -ne 0 ]; then
> sudo systemctl start psacct
> fi

Использование конструкции if/then/else

Конструкцию if/then можно расширить, чтобы, в зависимости от условий, выполнялись разные наборы действий. Для этого используется конструкция if/then/else.

if <CONDITION>; then
      <STATEMENT>
      ...
      <STATEMENT>
    else
      <STATEMENT>
      ...
      <STATEMENT>
fi

В следующем отрывке кода демонстрируется использование инструкции if/then/else для запуска службы psacct, если она неактивна, или остановки службы, если она активна.

[user@host ~]$ systemctl is-active psacct > /dev/null 2>&1
[user@host ~]$ if  [ $? -ne 0 ]; then
> sudo systemctl start psacct
> else
> sudo systemctl stop psacct
> fi

Использование конструкции if/then/elif/then/else

Конструкцию if/then/else можно расширить еще больше, чтобы проверялись несколько условий и выполнялись разные наборы действий. Пример такой конструкции приведен ниже.

if <CONDITION>; then
      <STATEMENT>
      ...
      <STATEMENT>
    elif <CONDITION>; then
      <STATEMENT>
      ...
      <STATEMENT>
    else
      <STATEMENT>
      ...
      <STATEMENT>
    fi

В этой условной конструкции Bash проверяет условия в порядке их появления. Если какое-либо условие соблюдается (true), Bash выполняет связанные с ним действия и пропускает оставшуюся часть условной конструкции. Если ни одно из условий не соблюдается, Bash выполняет действия, указанные в инструкции else.

В следующем отрывке кода демонстрируется использование инструкции if/then/elif/then/else для запуска клиента mysql, если активна служба mariadb, запуска клиента psql, если активна служба postgresql, или запуска клиента sqlite3, если службы mariadb и postgresql неактивны.

[user@host ~]$ systemctl is-active mariadb > /dev/null 2>&1
MARIADB_ACTIVE=$?
[user@host ~]$ sudo systemctl is-active postgresql > /dev/null 2>&1
POSTGRESQL_ACTIVE=$?
[user@host ~]$ if  [ "$MARIADB_ACTIVE" -eq 0 ]; then
> mysql
> elif  [ "$POSTGRESQL_ACTIVE" -eq 0 ]; then
> psql
> else
> sqlite3
> fi

Ссылки

Man-страница bash(1)