Подсказки по Linux
Функции сравнения и тестирования в Bash
Пояснения по конструкциям test , [, [[, ((, и if-then-else
Серия контента:
Этот контент является частью # из серии # статей: Подсказки по Linux
Этот контент является частью серии:Подсказки по Linux
Следите за выходом новых статей этой серии.
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, если тестируемый файл существует и имеет указанные характеристики.
Оператор | Характеристика |
---|---|
-d | Папка |
-e | Существует (также -a) |
-f | Стандартный файл |
-h | Символьная ссылка (также -L) |
-p | Именованный канал |
-r | Доступный вам для чтенения |
-s | Не пустой |
-S | Сокет |
-w | Доступный вам для записи |
-N | Был изменен со времени последнего прочтения |
Кроме представленных выше унарных тестов существуют бинарные операторы для сравнения двух файлов. Они показаны в таблице 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-коды, написание скриптов, программирование и компилирование)" из которого была взята данная статья, или обратитесь к другим ресурсам ниже. Не забудьте оценить эту страницу.
Ресурсы для скачивания
Похожие темы
- Оригинал этой статьи
- Просмотрите руководство "LPI exam 102 prep: Shells, scripting, programming, and compiling" (developerWorks, Январь 2007), чтобы найти более подробную информацию про кастомизацию Bash shell и скриптпнг в Linux. Оно является частью серии LPI exam prep tutorial series (Серии руководств LPI exam prep), которая рассказывает об основах Linux и помогает в подготовке к аттестации по специальности системного администратора.
- Прочитайте на developerWorks эти статьи, чтобы узнать о других способах работы с Bash:
- Bash в примерах, Часть 1: Основы программирования в Bourne again shell (bash)
- Bash в примерах, Часть 2: Дальнейшие основы bash программирования
- Bash в примерах, часть 3: О системе ebuild
- System Administration Toolkit: Get the most out of bash(Набор инструментов системного администрирования: добейтесь лучшего результата с bash)
- Working in the bash shell
- "Shell Command Language(Язык команд Shell)" - здесь вы найдете язык команд shell в формате The Open Group и IEEE.
- Найдите еще больше руководств для разработчиков Linux в разделе Linux на developerWorks .
- Скачайте испытываемое программное обеспечение IBM прямо с developerWorks.