Практическое написание сценариев командной оболочки Bash/Команда test
Глава | Ветвления → | |
Команда test | ||
Команда test служит для того, чтобы эмулировать условные выражения в языке командной оболочки. Самая первая реализация test была отдельной программой, которой передавалась условная конструкция. По договоренности, эта программа возвращала 0, если переданное условие истинно, и 1 — если ложно.
Так как все сценарии изобилуют условными проверками, команда получила более короткий псевдоним в виде квадратной скобки — [
. В таком виде команда описана в POSIX. Однако у оригинальной команды test специфичный перегруженный синтаксис, который часто приводит к ошибкам у начинающих программистов. В Bash поддерживается POSIX-совместимый test и вводится его усовершенствованная встроенная версия в виде оператора [[
. Далее по тексту, под командой test мы будем понимать как [
, так и [[
, если не требуется уточнение.
Если вы пишите не портируемые сценарии, исключительно для оболочки Bash, то следует отдавать предпочтение модернизированной версии test, так как она в целом удобнее. Тем не менее, следует помнить, что в портируемых сценариях вы должны пользоваться только POSIX совместимым вариантом. Вариант [[
кроме Bash так же поддерживается в Ksh и Zsh.
В этом разделе мы рассмотрим особенности работы модернизированной команды test.
Сравнение [ и [[
[ | [[ |
---|---|
# Сравнивание строк
[ a \> b ]
[ a \< b ]
[ a = b ]
[ a != b ]
|
# Сравнивание строк
[[ a > b ]]
[[ a < b ]]
[[ a = b ]] или [[ a == b ]]
[[ a != b ]]
|
# Сравнивание чисел
[ 5 -gt 10 ]
[ 5 -lt 10 ]
[ 5 -ge 10 ]
[ 5 -le 10 ]
[ 5 -eq 10 ]
[ 5 -ne 10 ]
|
# Сравнивание чисел
[[ 5 -gt 10 ]] # больше
[[ 5 -lt 10 ]] # меньше
[[ 5 -ge 10 ]] # больше или равно
[[ 5 -le 10 ]] # меньше или равно
[[ 5 -eq 10 ]] # равно
[[ 5 -ne 10 ]] # не равно
|
# Конъюнкция и дизъюнкция
[ -n "$var" -a -f "$var" ]
[ -n "$var" -o -f "$var" ]
# Примечание:
# Важно ставить кавычки для подставляемых
# переменных, если в их значениях есть
# пробелы.
|
# Конъюнкция и дизъюнкция
[[ -n $var && -f $var ]]
[[ -n $var || -f $var ]]
# Примечание:
# Кавычки ставить можно, но не обязательно:
# работает одинаково.
#
|
# Группировка условий
[ "$var" -eq 5 -a \( "$var1" -eq 6 -o "$var1" -eq 7 \) ]
|
# Группировка условий
[[ $var -eq 5 && ( $var1 -eq 6 || $var1 -eq 7 ) ]]
|
# Регулярные выражения и маскирование
# не поддерживаются
#
|
# Регулярные выражения и маскирование
[[ $name = a* ]] или [[ $name == a* ]]
[[ $name =~ ^John$ ]]
|
- Обратите внимание
- Для старой версии test следует осторожно использовать операторы лексикографического сравнивания
\>
и\<
, так как они являются одним из расширений стандарта POSIX. Другими словами, команда их может не поддерживать в старых системах. - Для старой версии test также следует осторожно использовать операторы
-a
,-o
и группировку, так как в POSIX помечено, что это устаревшие возможности. Вместо них POSIX рекомендует использовать несколько вызовов test с объединением их операторами&&
и/или||
. - Внутри
[[
не производится разбиение по разделителюIFS
и Globbing, поэтому подстановку переменных не обязательно закавычивать.
Особенности использования test
[править]В старых сценариях часто можно встретить такую запись
[ x"$var1" = x"$var2" ]
# или
[ "x$var1" = "x$var2" ]
Это обусловлено тем, что если переменная не определена, то она раскрывается в пустоту. С точки зрения команды test пустота не является аргументом, и если мы опустим символ x
(или любой другой), то в результате команда будет выглядеть так после подстановки значений (в следующем примере мы предполагаем, что не определена var1
)
var2=a_word
[ = a_word ] # если не определена var1
Это является синтаксической ошибкой, поэтому раньше использовали некоторый символ, который оставался на позиции, если переменная не раскрывалась в какое-то значение. В современных интерпретаторах допустимо опускать этот произвольный символ, но нужно обязательно кавычить переменные.
[ "$var1" = "$var2" ] # Правильно, но с очень старыми интерпретаторами может не заработать,
# если одна из переменных окажется не проинициализирована.
При использовании модернизированной версии test можно не кавычить обращения к переменной, но при этом нужно гарантировать, что переменная будет проинициализирована до исполнения проверки условия
[[ $var1 == $var2 ]] # Правильно, если инициализация гарантирована
# Примечание:
# Это конструкция работает всегда в Bash, по меньшей мере с версии 4.4.19(1)-release.
Если потенциально возможно такое, что переменная в условии может быть раскрыта в пустоту, следует использовать подстановку со значением по умолчанию
[[ ${var1:-} == ${var2:-} ]] # Работает всегда
В Bash 4 модернизированный test умеет самостоятельно обращаться к переменным по их именам, если над ними не выполняются лексикографические операции.
NUMBER=5
# В этом примере команда test сама попытается раскрыть переменную NUMBER.
if [[ NUMBER -ge 0 ]]; then
echo "The number is $NUMBER"
fi
# Это аналогично следующей записи.
if [[ $NUMBER -ge 0 ]]; then
echo "The number is $NUMBER"
fi
# Если переменная не определена, то она раскроется в пустоту.
# Но для операторов сравнений чисел пустота считается нулем, поэтому следующий
# код отработает.
unset NUMBER
if [[ NUMBER -ge 0 ]]; then
echo "The number is $NUMBER"
fi
# Вывод:
# The number is
Этой возможностью следует пользоваться аккуратно. Вообще, рекомендуется быть последовательным и не забывать про символ доллара для любых переменных.
Сравнивание строк и чисел
[править]В языке командной оболочки всего один тип данных — строковый. За интерпретацию аргументов как чисел полностью отвечает команда, которой они передаются.
Вы можете видеть, что для сравнивания чисел у test есть свои операторы. Без этих операторов числа сравниваются как строки, т.е. используются коды символов ASCII кодировки, а не сами числа. Приведем несколько поучительных примеров.
Следующее выражение будет выполняться всегда корректно
[[ 5 == 5 ]]
Тем не менее, сравниваются не сами числа, а код символа 5 в таблице кодировки ASCII. Таким образом, оператор ==
будет всегда работать так же как -eq
для чисел, однако, формально так делать не желательно.
Теперь рассмотрим такое выражение
[[ 99 > 888 ]] # Возвращает ИСТИНУ
Казалось бы очевидно, что 99 меньше 888, но такая проверка будет возвращать ИСТИНУ. Связано это с тем, что лексикографически строка 99 больше 888 (код символа 9 в таблице кодировки ASCII больше 8).
Если вы хотите сравнивать числа как числа, а не строки, следует использовать один из операторов команды test: -gt, -ge, -lt, -le, -eq, -ne
. Таким образом предыдущий пример следует писать так
[[ 99 -gt 888 ]] # Возвращает ЛОЖЬ, как и ожидалось
В Ksh, Bash и Zsh также есть еще одна разновидность команды test, которая может работать только с числами. В этой версии все операторы работают с числами, как это ожидается для чисел, но в портируемых скриптах этой возможностью пользоваться нельзя.
(( 5 == 5 ))
(( 4 > 3 ))
(( 3 < 4 ))
(( 21 >= 25 ))
(( 100 <= 255 ))
(( 6 != 5 ))
Эта версия test сложнее чем кажется и поддерживает разные операторы как арифметическая подстановка, но без вывода результата (см. Bash подстановки), например
(( 2 + 2 == 4 )) # ИСТИНА
Ветвления → |