Поиск по сайту:

Как использовать условные тесты с двойными скобками в Linux


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

Одиночные и двойные кронштейны

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

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

Если проверка верна, будет напечатано слово «Да».

test 15 -eq 15 && echo "Yes"
test 14 -eq 15 && echo "Yes"

Условные тесты с одной скобкой имитируют команду test. Они заключают выражение в скобки «[ ]» и работают так же, как команда test. По сути, это одна и та же программа, созданная из одного и того же исходного кода. Единственная рабочая разница заключается в том, как версии test и версии [ обрабатывают запросы на помощь.

Это из исходного кода:

/* Recognize --help or --version, but only when invoked in the
"[" form, when the last argument is not "]". Use direct
parsing, rather than parse_long_options, to avoid accepting
abbreviations. POSIX allows "[ --help" and "[ --version" to
have the usual GNU behavior, but it requires "test --help"
and "test --version" to exit silently with status 0. */

Мы можем увидеть эффект этого, обратившись за помощью к test и [ и проверив код ответа, отправленный в Bash.

test --help
echo $?
[ --help
echo $?

И test, и [ являются встроенными оболочками, то есть они встроены прямо в Bash. Но есть и отдельная бинарная версия [.

type test
type [
whereis [

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

Ключевые слова с двойными скобками поддерживаются Bash, но они недоступны в любой другой оболочке. Например, оболочка Korn их поддерживает, а старая добрая оболочка sh — нет. Все наши скрипты начинаются со строки:

#!/bin/bash

Это гарантирует, что мы вызываем оболочку Bash для запуска скрипта.

Встроенные функции и ключевые слова

Мы можем использовать программу compgen для получения списка встроенных функций:

compgen -b | fmt -w 70

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

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

Чтобы увидеть ключевые слова, мы можем использовать:

compgen -k | fmt -w 70

Ключевые слова [[ и ]] находятся в списке, поскольку [[ — это одно ключевое слово, а ]] это другое. Они представляют собой совпадающую пару, как и if и fi , а также case и esac .

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

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

Подстановка оболочки

Условные тесты с двойной скобкой могут использовать подстановку оболочки. Это означает, что звездочка «*» будет означать «что угодно».

Введите или скопируйте следующий текст в редактор и сохраните его в файле с именем «whelkie.sh».

#!/bin/bash

stringvar="Whelkie Brookes"

if [[ "$stringvar" == *elk* ]];
then
  echo "Warning contains seafood"
else
  echo "Free from molluscs"
fi

Чтобы сделать скрипт исполняемым, нам нужно использовать команду chmod с параметром -x (выполнить). Вам нужно будет сделать это со всеми сценариями в этой статье, если вы хотите их опробовать.

chmod +x whelkie.sh

Когда мы запускаем скрипт, мы видим, что строка «elk» была найдена в строке «Whelkie», независимо от того, какие другие символы ее окружают.

./whelkie.sh

Следует отметить, что мы не заключаем строку поиска в двойные кавычки. Если вы это сделаете, подстановка не произойдет. Строка поиска будет обработана буквально.

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

#!/bin/bash

stringvar="Jean-Claude van Clam"

if [[ "$stringvar" == *[cC]lam* ]];
then
  echo "Warning contains seafood."
else
  echo "Free from molluscs."
fi

Сохраните этот скрипт как «damme.sh» и сделайте его исполняемым. Когда мы запускаем его, условный оператор принимает значение true, и выполняется первое предложение оператора if.

./damme.sh

Цитаты строк

Ранее мы упоминали о заключении строк в двойные кавычки. Если вы это сделаете, подстановка оболочки не произойдет. Хотя соглашение гласит, что это хорошая практика, вам не нужно необходимо заключать строковые переменные в кавычки при использовании [[ и ]], даже если они содержать пробелы. Посмотрите на следующий пример. Обе строковые переменные $stringvar и $surname содержат пробелы, но ни одна из них не заключена в кавычки в условном выражении.

#!/bin/bash

stringvar="van Damme"
surname="van Damme"

if [[ $stringvar == $surname ]];
then
echo "Surnames match."
else
echo "Surnames don't match."
fi

Сохраните это в файл с именем «surname.sh» и сделайте его исполняемым. Запустите его, используя:

./surname.sh

Несмотря на то, что обе строки содержат пробелы, сценарий выполняется успешно, и условное выражение принимает значение true. Это полезно при работе с путями и именами каталогов, которые содержат пробелы. Здесь параметр -d возвращает значение true, если переменная содержит допустимое имя каталога.

#!/bin/bash

dir="/home/dave/Documents/Needs Work"

if [[ -d ${dir} ]];
then
  echo "Directory confirmed"
else
  echo "Directory not found"
fi

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

./dir.sh

Ошибки при объединении имен файлов

Интересное различие между [ ] и [[ ]] относится к именам файлов с подстановкой в них. Форма «*.sh» будет соответствовать всем файлам скриптов. Использование одинарных квадратных скобок [ ] не работает, если нет одного файла сценария. При обнаружении более одного скрипта выдается ошибка.

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

#!/bin/bash

if [ -a *.sh ];
then
  echo "Found a script file"
else
  echo "Didn't find a script file"
fi

Мы сохранили этот текст в «script.sh» и сделали его исполняемым. Мы проверили, сколько скриптов было в каталоге, а затем запустили скрипт.

ls
./script.sh

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

ls
./script.sh

Условная проверка возвращает true, и скрипт не вызывает ошибки. Редактирование сценария для использования двойных скобок обеспечивает третий тип поведения.

#!/bin/bash

if [[ -a *.sh ]];
then
  echo "Found a script file"
else
  echo "Didn't find a script file"
fi

Мы сохранили это в файл с именем «dscript.sh» и сделали его исполняемым. Запуск этого скрипта в каталоге с большим количеством скриптов не приводит к ошибке, но скрипт не может распознать какие-либо файлы скриптов.

Условный оператор, использующий двойные скобки, принимает значение true только в маловероятном случае, когда в каталоге есть файл с именем «*.sh».

./dscript.sh

Логические И и ИЛИ

Двойные скобки позволяют использовать && и || в качестве логических операторов И и ИЛИ.

Этот скрипт должен преобразовать условный оператор в true, поскольку 10 действительно равно 10, и 25 меньше 26.

#!/bin/bash

first=10
second=25

if [[ first -eq 10 && second -lt 26 ]];
then
  echo "Condition met"
else
  echo "Condition failed"
fi

Сохраните этот текст в файл с именем «and.sh», сделайте его исполняемым и запустите с помощью:

./and.sh

Скрипт выполняется так, как мы и ожидали.

На этот раз мы будем использовать оператор ||. Условный оператор должен разрешиться как истина, потому что, хотя 10 не больше 15, 25 все равно меньше 26. Если первое сравнение или второе сравнение верно, условное выражение в целом принимает истинное значение.

Сохраните этот текст как «or.sh» и сделайте его исполняемым.

#!/bin/bash

first=10
second=25

if [[ first -gt 15 || second -lt 26 ]];
then
  echo "Condition met."
else
  echo "Condition failed."
fi
./or.sh

Регулярные выражения

Условные операторы с двойными скобками позволяют использовать оператор =~, который применяет шаблоны поиска регулярных выражений в строке к другой половине оператора. Если регулярное выражение выполняется, условное выражение считается истинным. Если регулярное выражение не находит совпадений, условное выражение принимает значение false.

Сохраните этот текст в файл с именем «regex.sh» и сделайте его исполняемым.

#!/bin/bash

words="one two three"
WordsandNumbers="one 1 two 2 three 3"
email="dave@fabricateddomain.co.uk"

mask1="[0-9]"
mask2="[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,4}"

if [[ $words =~ $mask1 ]];
then
  echo "\"$words\" contains digits."
else
  echo "No digits found in \"$words\"."
fi

if [[ $WordsandNumbers =~ $mask1 ]];
then
  echo "\"$WordsandNumbers\" contains digits."
else
  echo "No digits found in \"$WordsandNumbers\"."
fi

if [[ $email =~ $mask2 ]];
then
  echo "\"$email\" is a valid e-mail address."
else
  echo "Couldn't parse \"$email\"."
fi

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

Второй набор двойных скобок снова использует строковую переменную $mask1 в качестве регулярного выражения, но на этот раз она использует ее со строковой переменной $WordsandNumbers.

Последний набор двойных скобок использует более сложную маску регулярного выражения в строковой переменной $mask2 .

  • [A-Za-z0-9._%+-]+: соответствует любому символу, который является прописной или строчной буквой, или любой цифрой от нуля до девяти, или точкой, знак подчеркивания, знак процента или знак плюс или минус. «+» вне «[]» означает повторение этих совпадений для всех найденных символов.
  • @: соответствует только символу «@».
  • [A-Za-z0-9.-]+: соответствует любому символу, который является прописной или строчной буквой, любой цифрой от нуля до девяти, точкой или дефисом. «+» вне «[ ]» означает повторение этих совпадений для всех найденных символов.
  • .: соответствует «.» только символ.
  • [A-Za-z]{2,4}: соответствует любой прописной или строчной букве. «{2,4}» означает совпадение не менее двух символов и не более четырех.

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

Сохраните текст сценария в файл с именем «regex.sh» и сделайте его исполняемым. Когда мы запускаем скрипт, мы получаем этот вывод.

./regex.sh

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

Второй условный оператор выполнен успешно, поскольку строковая переменная $WordsandNumbers действительно содержит цифры.

Окончательный условный оператор завершается успешно, т. е. принимает значение true, поскольку адрес электронной почты имеет правильный формат.

Всего одно условие

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

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