Приоритет логических операторов оболочки &&, ||

Я пытаюсь понять, как работает приоритет логического оператора в bash. Например, я ожидал, что следующая команда ничего не даст.

true || echo aaa && echo bbb 

Однако, вопреки моему ожиданию, bbb печатается.

Может кто-нибудь, пожалуйста, объясните, как я могу понять составные && и || операторов в bash?

3 Solutions collect form web for “Приоритет логических операторов оболочки &&, ||”

На многих компьютерных языках операторы с одинаковым приоритетом являются лево-ассоциативными . То есть, при отсутствии группирующих структур, сначала выполняются самые левые операции. Баш не является исключением из этого правила.

Это важно, потому что в Bash, && и || имеют тот же приоритет.

Итак, что происходит в вашем примере, так это то, что самая левая операция ( || ) выполняется сначала:

 true || echo aaa 

Так как true верно, то || короткое замыкание оператора, и весь оператор считается истинным без необходимости оценивать echo aaa как и следовало ожидать. Теперь остается сделать самую правую операцию:

 (...) && echo bbb 

Поскольку первая операция оценивается как истина (т. Е. Имеет статус выхода 0), она, как если бы вы выполняли

 true && echo bbb 

поэтому && не будет && короткое замыкание, поэтому вы видите bbb эхом.

Вы получите такое же поведение с

 false && echo aaa || echo bbb 

Примечания, основанные на комментариях

  • Следует отметить, что правило левой ассоциативности выполняется только тогда, когда оба оператора имеют одинаковый приоритет. Это не тот случай, когда вы используете эти операторы в сочетании с такими ключевыми словами, как [[...]] или ((...)) или используйте операторы -o и -a качестве аргументов для test или [ команд. В таких случаях И ( && или -a ) имеет приоритет над OR ( || или -o ). Спасибо за комментарий Стефана Чазеласа, чтобы прояснить этот момент.
  • Кажется, что в C и C-подобных языках && имеет более высокий приоритет, чем || вероятно, поэтому вы ожидали, что ваша оригинальная конструкция будет вести себя как

     true || (echo aaa && echo bbb). 

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

  • Могут также быть случаи, когда оцениваются все 3 выражения. Если первая команда возвращает ненулевой статус выхода, значение || не будет короткого замыкания и продолжит выполнение второй команды. Если вторая команда вернется с нулевым статусом выхода, то && также не будет && короткое замыкание, и будет выполнена третья команда. Благодаря комментарию Игнасио Васкес-Абрамса за это.

Если вы хотите, чтобы несколько вещей зависели от вашего состояния, сгруппируйте их:

 true || { echo aaa && echo bbb; } 

Это ничего не возвращает, пока

 true && { echo aaa && echo bbb; } 

эхо обе строки.


Причина, по которой это происходит, намного проще, чем разбирается Джозеф. Помните, что делает Bash с || и && . Это все о статусе возврата предыдущей команды. Литературным способом просмотра вашей необработанной команды является:

 ( true || echo aaa ) && echo bbb 

Первая команда ( true || echo aaa ) выходит с 0 .

 $ true || echo aaa; echo $? 0 $ true && echo aaa; echo $? aaa 0 $ false && echo aaa; echo $? 1 $ false || echo aaa; echo $? aaa 0 

&& и || операторы не являются точными встроенными заменами для if-then-else. Хотя, если их использовать осторожно, они могут совершать многое одно и то же.

Один тест прост и однозначен …

 [[ A == A ]] && echo TRUE # TRUE [[ A == B ]] && echo TRUE # [[ A == A ]] || echo FALSE # [[ A == B ]] || echo FALSE # FALSE 

Однако попытка добавить несколько тестов может привести к неожиданным результатам …

 [[ A == A ]] && echo TRUE || echo FALSE # TRUE (as expected) [[ A == B ]] && echo TRUE || echo FALSE # FALSE (as expected) [[ A == A ]] || echo FALSE && echo TRUE # TRUE (as expected) [[ A == B ]] || echo FALSE && echo TRUE # FALSE TRUE (huh?) 

Почему оба FALSE и TRUE эхом?

Что здесь происходит, так это то, что мы не поняли, что && и || являются перегруженными операторами, которые действуют по-разному в условных тестовых скобках [[ ]] чем они есть в списке И и ИЛИ (условное исполнение), который мы здесь имеем.

Из справочной страницы bash (отредактировано) …

Списки

Список представляет собой последовательность из одного или нескольких конвейеров, разделенных одним из операторов: &, && или ││ и необязательно завершенных одним из;, & или. Из этих операторов списка && и ││ имеют одинаковый приоритет, за которым следует; и &, которые имеют одинаковый приоритет.

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

Если команда завершена оператором управления &, оболочка выполняет команду в фоновом режиме в подоболочке. Оболочка не ждет завершения команды, а статус возврата равен 0. Команды, разделенные символом a; выполняются последовательно; оболочка ожидает, что каждая команда будет завершена по очереди. Статус возврата – это статус выхода последней выполненной команды.

Списки И и ИЛИ представляют собой последовательности одного из нескольких конвейеров, разделенных операциями управления && и operators соответственно. Списки AND и OR выполняются с левой ассоциативностью.

Список AND имеет форму …
command1 && command2
Command2 выполняется, если и только если команда1 возвращает статус выхода из нуля.

Список OR имеет форму …
command1 ││ command2
Command2 выполняется тогда и только тогда, когда command1 возвращает ненулевой статус выхода.

Возвращаемым статусом списков AND и OR является статус выхода последней команды, выполненной в списке.

Возвращаясь к нашему последнему примеру …

 [[ A == B ]] || echo FALSE && echo TRUE [[ A == B ]] is false || Does NOT mean OR! It means... 'execute next command if last command return code(rc) was false' echo FALSE The 'echo' command rc is always true (ie it successfully echoed the word "FALSE") && Execute next command if last command rc was true echo TRUE Since the 'echo FALSE' rc was true, then echo "TRUE" 

Хорошо. Если это так, то почему последний пример повторяет что-нибудь вообще?

 [[ A == A ]] || echo FALSE && echo TRUE [[ A == A ]] is true || execute next command if last command rc was false. echo FALSE Since last rc was true, shouldn't it have stopped before this? Nope. Instead, it skips the 'echo FALSE', does not even try to execute it, and continues looking for a `&&` clause. && ... which it finds here echo TRUE ... so, since `[[ A == A ]]` is true, then it echos "TRUE" 

Риск логических ошибок при использовании более одного && или || в списке команд довольно высок.

рекомендации

Единственный && или || в списке команд работает так, как ожидалось, так что это довольно безопасно. Если это ситуация, когда вам не нужна фраза else, что-то вроде следующего может быть понятнее (фигурные скобки необходимы для группировки последних двух команд) …

 [[ $1 == --help ]] && { echo "$HELP"; exit; } 

Множество && и || операторы, где каждая команда, кроме последней, является тестом (то есть внутри скобок [[ ]] ), как правило, также безопасны, поскольку все, кроме последнего оператора, ведут себя так, как ожидалось. Последний оператор больше похож на предложение then или else .

  • Почему тильда (~) не расширяется внутри двойных кавычек?
  • Unix> перезаписать команду?
  • Bash clear command weird behavior удаляет буфер прокрутки.
  • Сценарий оболочки, использующий функцию (), не находящую команду
  • Преобразуйте аргумент в верхний регистр, чтобы передать его как переменную
  • Переадресация ввода-вывода и головная команда
  • В чем смысл правила «в спецификации POSIX не указано, является ли оно ASSIGNMENT_WORD или WORD»
  • Выполнение скрипта в .zshrc
  • Замена процесса в GNU Makefiles
  • Как автоматически cd после git clone?
  • Помогите мне понять эту конструкцию echo / spawn / send / expect
  • Linux и Unix - лучшая ОС в мире.