Подсказки по Linux: Функции сравнения и тестирования в Bash

Пояснения по конструкциям test , [, [[, ((, и if-then-else

Вы запутались во множестве способов тестирования и сравнения в Bash shell? Это руководство поможет вам освоиться с различными тестами для файлов, строк и чисел. Вы узнаете, когда и как стоит использовать конструкции с test, [ ], [[ ]], (( )) или if-then-else.

Bash shell сгодня доступен на многих Linux® и UNIX® системах и является по умолчанию основным shell-кодом в Linux. Bash предоставляет широчайшие возможности для программирования, в том числе расширенное число фунций для тестирования типов файлов и атрибутов, равно как и всяческие возможности для сравнения строк и чисел, доступные в большинстве языков программирования. Для продвинутого пользователя shell крайне важно разбираться в различных типах тестов и знать то, что shell может интерпретировать некоторые операторы как свои метасимволы. Эта статья представляет собой извлечение из руководства developerWorks LPI exam 102 prep: Shells, scripting, programming, and compiling (exam 102 prep: Shell-коды, написание скриптов, программирование и компилирование) и показывает, как разобраться в использовании операций проверки и сравнения в Bash shell.

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

Тесты

При работе с любым языком программирования, после того, как вы научитесь приписывать значения переменным и передавать параметры, вам понадобится знать, как тестировать эти значения и параметры. В shell-кодах тесты, как и все прочие команды определяют статус выдачи. При этом выражение test даже является встроенной командой!

test и [

Встроенная команда test возвращает 0 (True) или 1 (False), в зависимости от определения выражения expr. Также вы можете использовать квадратные скобки: test expr и [expr] представляют из себя эквивалентные выражения. Вы можете просмотреть возвращаемое значение с помощью $?; вы можете использовать возвращаемое значение с помощью && и ||; вы можете также протестировать это значение с помощью различных условных конструкций, которые рассматриваются далее в этом руководстве.

Листинг 1. Некоторые простые тесты
[ian@pinguino ~]$ test 3 -gt 4 && echo True || echo false
false
[ian@pinguino ~]$ [ "abc" != "def" ];echo $?
0
[ian@pinguino ~]$ test -d "$HOME" ;echo $?
0

В первом примере листинга 1, оператор -gt выполняет арифметическое сравнение двух строковых значений. Во втором примере с помощью альтернативной тестовой формы [ ] оценивается (не)равенство двух строк. В последнем примере значение переменной HOME проверяется с целью увидеть, папка это или нет, при этом используется унарный оператор -d.

Вы можете сравнивать два числовых значения с помощью операторов -eq, -ne, -lt, -le, -gt, or -ge, означающих соответственно "равно", "неравно", "меньше", "меньше либо равно", "больше" и "больше либо равно".

Вы можете сравнивать строки на равенство и неравенство, а также на то, будет ли первая строка поставлена до или после второй при сортировке. Для всего этого используются, соответственно, операторы =, !=, < и >. Унарный оператор -z проверяет, не пуста ли строка, тогда как оператор -n или вообще отсутствие оператора возвращает True если строка не пуста.

Замечание: операторы < и > также используются shell для перенаправления. Этого надо избегать с помощью \< или \>. Листинг 2 содержит в себе еще несколько примеролв тестов строк. Проверьте, как они работают.

Листинг 2. Некоторые тесты строк
[ian@pinguino ~]$ test "abc" = "def" ;echo $?
1
[ian@pinguino ~]$ [ "abc" != "def" ];echo $?
0
[ian@pinguino ~]$ [ "abc" \< "def" ];echo $?
0
[ian@pinguino ~]$ [ "abc" \> "def" ];echo $?
1
[ian@pinguino ~]$ [ "abc" \<"abc" ];echo $?
1
[ian@pinguino ~]$ [ "abc" \> "abc" ];echo $?
1

Некоторые наиболее часто используемые тесты файлов показаны в таблице 1. Результат теста это True, если тестируемый файл существует и имеет указанные характеристики.

Таблица 1. Некоторые часто используемые тесты файлов
ОператорХарактеристика
-dПапка
-eСуществует (также -a)
-fСтандартный файл
-hСимвольная ссылка (также -L)
-pИменованный канал
-rДоступный вам для чтенения
-sНе пустой
-SСокет
-wДоступный вам для записи
-NБыл изменен со времени последнего прочтения

Кроме представленных выше унарных тестов существуют бинарные операторы для сравнения двух файлов. Они показаны в таблице 2.

Таблица 2. Тестирование пар файлов
ОператорTrue если
-ntПроверяет, является ли file1 более новым, чем file 2. Для этого и для следующего сравнения используется дата последнего изменения.
-otПроверяет, является ли file1 более старым, чем file 2.
-efПроверяет, является ли file1 жесткой ссылкой на file2.

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

Оператор -o позволяет вам тестировать различные опции shell, которые могут быть установлены командой set -o option, которая возвращает True (0), если опция установлена и False (1) в противном случае, как показано в листинге 3.

Листинг 3. Тестирование опций shell
[ian@pinguino ~]$ set +o nounset
[ian@pinguino ~]$ [ -o nounset ];echo $?
1
[ian@pinguino ~]$ set -u
[ian@pinguino ~]$ test  -o nounset; echo $?
0

Наконец, опции -a и -o соединяют выражения как логическое И и ИЛИ соответственно, а унарный оператор ! делает смысл теста противоположным. Вы можете использовать круглые скобки, чтобы группировать выражения и выполнять операции не по порядку установленного по умолчанию старшинства. Помните, что shell, как правило, запускает выражение в скобках в электронной подоболочке, поэтому вам придется вместо просто скобок писать \( и \) или заключать описанные выше операторы в одиночные или двойные кавычки. Листинг 4 показывает применение к выражению законов де Моргана.

Листинг 4. Конъюнкция и дизъюнкция тестов
[ian@pinguino ~]$ test "a" != "$HOME" -a 3 -ge 4 ; echo $?
1
[ian@pinguino ~]$ [ ! \( "a" = "$HOME" -o 3 -lt 4 \) ]; echo $?
1
[ian@pinguino ~]$ [ ! \( "a" = "$HOME" -o '(' 3 -lt 4 ')' ")" ]; echo $?
1

(( и [[

Команда test предоставляет широчайшие возможности, но является несколько неудобной в использовании из-за необходимости делать переходы в коде с помощью \ и из-за различиях в сравнениях строк и чисел. К счастью, bash располагает двумя другими способами тестирования, которые покажутся более естественными для тех, кто знаком с синтаксисом C, C++ или Java®.

Составная команда(( )) оценивает арифметическое выражение и устанавливает статус выдачи равный 1, если выражение равно 0, или статус выдачи равный 0, если выражение имеет ненулевое значение. Вам ненужно ставить \ перед операторами между (( и )). Арифметика работает с целыми числами. Деление на 0 вызывает ошибку, но переполнение этого не делает. Вы выполняете обычные для языка C арифметические, логические и побитовые операции. Команда let также может запускать одно или несколько арифметических выражениий. Она обычно используется для того, чтобы приписывать значения арифметическим переменным.

Листинг 5. Присваивание и тестирование арифметических выражений
[ian@pinguino ~]$ let x=2 y=2**3 z=y*3;echo $? $x $y $z
0 2 8 24
[ian@pinguino ~]$ (( w=(y/x) + ( (~ ++x) & 0x0f ) )); echo $? $x $y $w
0 3 8 16
[ian@pinguino ~]$ (( w=(y/x) + ( (~ ++x) & 0x0f ) )); echo $? $x $y $w
0 4 8 13

Как и в случае с (( )), составная команда [[ ]] позволяет вам использовать более естественный синтаксис для тестирования имен файлов и строк. Вы можете объединять тесты, которые разрешены для команды test с помощью круглых скобок и логических операторов.

Листинг 6. Использование составного оператора [[
[ian@pinguino ~]$ [[ ( -d "$HOME" ) && ( -w "$HOME" ) ]] &&  
>  echo "home is a writable directory"
home is a writable directory

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

Листинг 7. Тесты с [[ и групповым символом
[ian@pinguino ~]$ [[ "abc def .d,x--" == a[abc]*\ ?d* ]]; echo $?
0
[ian@pinguino ~]$ [[ "abc def c" == a[abc]*\ ?d* ]]; echo $?
1
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* ]]; echo $?
1

Вы можете даже делать арифметические тесты внутри составных операторов с [[, но делайте это с осторожностью. Если только они не находятся внутри составного оператора ((, операторы < и > будут сравнивать операнды как строки и проверять их порядок в зависимости от текущей последовательности сортировки. Листинг 8 дает некоторые примеры этого.

Листинг 8. Осуществление арифметических тестов с помощью [[
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || (( 3 > 2 )) ]]; echo $?
0
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || 3 -gt 2 ]]; echo $?
0
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || 3 > 2 ]]; echo $?
0
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || a > 2 ]]; echo $?
0
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || a -gt 2 ]]; echo $?
-bash: a: unbound variable

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

С помощью описанных выше тестов и операторов контроля && и || вы можете решить немало задач программирования. Кроме всего этого bash включает в себя и более привычные конструкции с if, then, else и с case. Когда вы ознакомитесь с ними, можно будет перейти к конструкциям с циклами, что существенно расширит набор средств, которые вы сможете использовать.

Консрукции с if, then, else

Команда if в bash является составной командой которая проверяет выдаваемое значение теста или команды ($?), а затем ветвится в зависимости от того равно ли это проверяемое значение True (0) или False (не 0). Вышеописанные тесты возвращают только значения 0 или 1, но команды могут возвращать и другие значения. Больше вы узнаете об этом в руководстве LPI exam 102 prep: Shells, scripting, programming, and compiling (LPI exam 102 prep: Shell-коды, написание скриптов, программирование и компилирование.

Составная команда if в bash всегда содержит в себе предложение с then со списком команд которые должны быть выполнены, если тест или команда сразу после if возвращает 0. Команда if также может содержать одно или несколько предложений с elif, каждое из которых содержит в себе дополнительный тест и предложение с then и списком команд. Далее if может содержать завершающее предложение с else и списком команд, оторые надо выполнить, если ни изначальный тест, ни все тесты в предложениях с elif не дали результат True. Наконец, if завершается выражением fi, обозначающим окончание этой конструкции.

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

Листинг 9. Вычисление выражений с if, then, else
[ian@pinguino ~]$ function mycalc ()
> {
>   local x
>   if [ $# -lt 1 ]; then
>     echo "This function evaluates arithmetic for you if you give it some"
>   elif (( $* )); then
>     let x="$*"
>     echo "$* = $x"
>   else
>     echo "$* = 0 or is not an arithmetic expression"
>   fi
> }
[ian@pinguino ~]$ mycalc 3 + 4
3 + 4 = 7
[ian@pinguino ~]$ mycalc 3 + 4**3
3 + 4**3 = 67
[ian@pinguino ~]$ mycalc 3 + (4**3 /2)
-bash: syntax error near unexpected token `('
[ian@pinguino ~]$ mycalc 3 + "(4**3 /2)"
3 + (4**3 /2) = 35
[ian@pinguino ~]$ mycalc xyz
xyz = 0 or is not an arithmetic expression
[ian@pinguino ~]$ mycalc xyz + 3 + "(4**3 /2)" + abc
xyz + 3 + (4**3 /2) + abc = 35

Калькулятор использует выражение local, чтобы объявить x локальной переменной, которая используется только внутри функции mycalc. Функция let имеет несколько возможных опций, как и родственная ей функция declare. Для более детального ознакомления посмотрите страницы с руководствами по bash или используйте команду help let.

Как вы видели в листинге 9, необходимо ставить \ перед выражениями, содержащими метасимволы shell, такие как (, ), *, > и <. И все же, вы получили вполне удобный небольшой калькулятор для арифметических вычислений с помощью shell.

Возможно, вы обратили особое внимание на предложение с elseи на два последних примера в листинге 9. Как вы видите, передавать xyz на вход функции mycalc не будет ошибкой, но такое выражение будет вычислено как 0. Эта функция не умеет определять значения символов в последнем примере и не может выдать пользователю предупреждение. Вы можете использовать тест для сопоставления строк с образцом, например
[[ ! ("$*" == *[a-zA-Z]* ]]
(или в другой форме в зависимости от вашей локали), чтобы распознавать выражения, содержащие алфавитные символы, но это также не даст вам испльзовать шестнадцатиричную запись вводных данных (вы могли бы, например, записать 15 как 0x0f в шестнадцатиричной записи). Вообще shell позволяет использовать основания систем счисления вплоть до 64 (с использованием нотации base#value, где base - основание, а value - значение), так что вы можете использовать любой алфавитный символ, а также _ и @ для записи вводных данных. Для восьмеричной и шестнадцатеричной систем счисления используется обычная нотация со стоящими перед записью числа символамим 0 и 0x (или 0X) соответственно. Листинг 10 дает некоторые примеры этого.

Листинг 10. Числовые вычисления с различными основаниями системы счисления
[ian@pinguino ~]$ mycalc 015
015 = 13
[ian@pinguino ~]$ mycalc 0xff
0xff = 255
[ian@pinguino ~]$ mycalc 29#37
29#37 = 94
[ian@pinguino ~]$ mycalc 64#1az
64#1az = 4771
[ian@pinguino ~]$ mycalc 64#1azA
64#1azA = 305380
[ian@pinguino ~]$ mycalc 64#1azA_@
64#1azA_@ = 1250840574
[ian@pinguino ~]$ mycalc 64#1az*64**3 + 64#A_@
64#1az*64**3 + 64#A_@ = 1250840574

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

Выражение elif очень удобно. Оно помогает вам в написании скриптов тем, что усеньшает количество отступов в программе. Возможно, вас удивит выдача команды type для функции mycalc, показанная в листинге 11.

Листинг 11. Type mycalc
[ian@pinguino ~]$ type mycalc
mycalc is a function
mycalc ()
{
    local x;
    if [ $# -lt 1 ]; then
        echo "This function evaluates arithmetic for you if you give it some";
    else
        if (( $* )); then
            let x="$*";
            echo "$* = $x";
        else
            echo "$* = 0 or is not an arithmetic expression";
        fi;
    fi
}

Конечно, вы можете делать арифметические вычисления с помощью shell просто с помощью команды $(( expression )), где expression - арифметическое выражение, вместе с командой echo , как это показано в листинге 12. Для этого вам не нужно было изучать различные функции и тесты. Отметьте все же, что shell не интерпретирует обычным образом метасимволы, такие как *, когда они стоят внутри выражения типа (( expression )) или [[ expression ]].

Листинг 12. Числовые вычисления в shell с помощью команд echo и $(( ))
[ian@pinguino ~]$  echo $((3 + (4**3 /2)))
35

Узнайте об этом больше

Если вы захотите узнать больше о Bash скриптинге в Linux, прочитайте руководство "LPI exam 102 prep: Shells, scripting, programming, and compiling (LPI exam 102 prep: Shell-коды, написание скриптов, программирование и компилирование)" из которого была взята данная статья, или обратитесь к другим ресурсам ниже. Не забудьте оценить эту страницу.

Ресурсы

Научиться

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



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

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, AIX и UNIX, Open source
ArticleID=240940
ArticleTitle=Подсказки по Linux: Функции сравнения и тестирования в Bash
publish-date=07172007