Простые логические операторы в Bash

256

У меня есть пара переменных, и я хочу проверить следующее условие (записанное словами, затем моя неудачная попытка сценария bash):

if varA EQUALS 1 AND ( varB EQUALS "t1" OR varB EQUALS "t2" ) then 

do something

done.

И в моей неудачной попытке я придумал:

if (($varA == 1)) && ( (($varB == "t1")) || (($varC == "t2")) ); 
  then
    scale=0.05
  fi
Amit
источник

Ответы:

682

То, что вы написали, на самом деле почти работает (это будет работать, если бы все переменные были числами), но это совсем не идиоматический способ.

  • (…)круглые скобки обозначают подоболочку . То, что внутри них, не является выражением, как во многих других языках. Это список команд (как и в скобках). Эти команды выполняются в отдельном подпроцессе, поэтому любое перенаправление, присваивание и т. Д., Выполняемые внутри скобок, не влияют за пределы скобок.
    • С ведущим знаком доллара $(…)- подстановка команды : в скобках есть команда, а вывод команды используется как часть командной строки (после дополнительных расширений, если подстановка не заключена в двойные кавычки, но это уже другая история ) ,
  • { … }Скобки подобны скобкам в том смысле, что они группируют команды, но они влияют только на синтаксический анализ, а не на группировку. Программа x=2; { x=4; }; echo $xпечатает 4, а x=2; (x=4); echo $xпечатает 2. (Также скобки требуют пробелов вокруг них и точки с запятой перед закрытием, тогда как круглые скобки - нет. Это просто синтаксическая ошибка).
    • С ведущим знаком доллара, ${VAR}это расширение параметра , расширяющееся до значения переменной, с возможными дополнительными преобразованиями.
  • ((…))двойные скобки окружают арифметическую инструкцию , то есть вычисление на целых числах, с синтаксисом, похожим на другие языки программирования. Этот синтаксис в основном используется для назначений и в условных выражениях.
    • Тот же синтаксис используется в арифметических выражениях $((…)), которые расширяются до целочисленного значения выражения.
  • [[ … ]]двойные скобки окружают условные выражения . Условные выражения в основном построены на операторах, таких как -n $variableпроверка, является ли переменная пустой, и -e $fileпроверка, существует ли файл. Существуют также операторы равенства строк: "$string1" == "$string2"(имейте в виду, что правая часть является шаблоном, например, [[ $foo == a* ]]тесты, если $fooначинается с, в aто время как [[ $foo == "a*" ]]тесты, если $fooточно a*), и знакомые !, &&и ||операторы для отрицания, соединения и дизъюнкции, а также круглые скобки для группировки. Обратите внимание, что вам нужен пробел вокруг каждого оператора (например [[ "$x" == "$y" ]], не [[ "$x"=="$y" ]]), а также пробел или символ, как ;внутри, так и вне скобок (например [[ -n $foo ]], не[[-n $foo]]).
  • [ … ]одиночные скобки представляют собой альтернативную форму условных выражений с большим количеством причуд (но более старых и более переносимых). Не пишите пока что; начать беспокоиться о них, когда вы найдете сценарии, которые их содержат.

Это идиоматический способ написать свой тест на bash:

if [[ $varA == 1 && ($varB == "t1" || $varC == "t2") ]]; then

Если вам нужна переносимость на другие оболочки, это будет правильным способом (обратите внимание на дополнительные кавычки и отдельные наборы скобок вокруг каждого отдельного теста и использование традиционного =оператора, а не ==варианта ksh / bash / zsh ):

if [ "$varA" = 1 ] && { [ "$varB" = "t1" ] || [ "$varC" = "t2" ]; }; then
Жиль "ТАК - перестань быть злым"
источник
31
Отличный пост, резюме в скобках просто идеальное.
KomodoDave
10
Лучше использовать, ==чтобы отличить сравнение от присвоения переменной (что также =)
Уилл Шеппард
1
О, я имел в виду одинарные (круглые) скобки, извините за путаницу. Те, что в [[ $varA = 1 && ($varB = "t1" || $varC = "t2") ]], не запускают подпроцесс, хотя первый пункт маркировки прямо говорит: «То, что внутри [круглых скобок] , не является выражением, как во многих других языках» - но это, безусловно, здесь! Это, вероятно, очевидно для опытного мастера Bash, но даже не для меня, сразу. Путаница может возникнуть из- за того , что в выражении могут использоваться одиночные скобки if, а не в выражениях в двойных скобках.
Питер - Восстановить Монику
2
@protagonist на ==самом деле не «более идиоматичен», чем =. Они имеют то же значение, но ==это вариант ksh, также доступный в bash и zsh, тогда как =является переносимым. На самом деле нет никаких преимуществ в использовании ==, и это не работает в простой ш.
Жиль "ТАК - перестать быть злым"
2
@WillSheppard Уже есть много других различий между сравнением и назначением: сравнение находится в квадратных скобках, назначение - нет; сравнение имеет пробелы вокруг оператора, присваивание - нет; присваивание имеет имя переменной слева, сравнение редко имеет что-то похожее на имя переменной слева, и вы можете и должны ставить кавычки в любом случае. Вы можете писать ==внутри [[ … ]], если хотите, но =у вас есть преимущество в том, что вы тоже работаете [ … ], поэтому я не рекомендую вообще использовать его ==.
Жиль "ТАК ... перестать быть злым"
34

очень близко

if [[ $varA -eq 1 ]] && [[ $varB == 't1' || $varC == 't2' ]]; 
  then 
    scale=0.05
  fi

должно сработать.

ломая это

[[ $varA -eq 1 ]] 

целочисленное сравнение, где как

$varB == 't1'

это сравнение строк. в противном случае я просто правильно группирую сравнения.

Двойные квадратные скобки ограничивают условное выражение. И я нахожу следующее чтение этой темы хорошим: «(IBM) Demystify test, [, [[, ((и if-then-else»)

matchew
источник
Просто чтобы быть уверенным: цитирование 't1'не нужно, верно? Потому что в отличие от арифметических инструкций в двойных скобках, где t1будет переменная, t1в условном выражении в двойных скобках есть просто литеральная строка. Т.е. [[ $varB == 't1' ]]точно так же, как [[ $varB == t1 ]], верно?
Питер - Восстановить Монику
6

Очень портативная версия (даже для устаревшей оболочки Bourne):

if [ "$varA" = 1 -a \( "$varB" = "t1" -o "$varB" = "t2" \) ]
then    do-something
fi

Это имеет дополнительное качество запуска только одного подпроцесса (который является процессом [), независимо от вида оболочки.

Замените =на, -eqесли переменные содержат числовые значения, например

  • 3 -eq 03 верно, но
  • 3 = 03ложно (сравнение строк)
JP Tosoni
источник
3

Вот код для короткой версии оператора if-then-else:

( [ $a -eq 1 ] || [ $b -eq 2 ] ) && echo "ok" || echo "nok"

Обратите внимание на следующее:

  1. ||и &&операнды внутри, если условие (т.е. между круглыми скобками) являются логическими операндами (или / и)

  2. ||и &&операнды снаружи, если условие означает то / еще

Практически в заявлении говорится:

если (a = 1 или b = 2), тогда «ок», иначе «нок»

тонкослойной хроматографии
источник
Скобка ( ... )создает подоболочку. Может хотеть использовать скобки { ... }вместо этого. Любое состояние, созданное в подоболочке, не будет видно в вызывающей стороне.
Клинт Пахл
0
if ([ $NUM1 == 1 ] || [ $NUM2 == 1 ]) && [ -z "$STR" ]
then
    echo STR is empty but should have a value.
fi
AlikElzin-kilaka
источник