Во многих компьютерных языках операторы с одинаковым приоритетом являются левоассоциативными . То есть при отсутствии группирующих структур самые левые операции выполняются первыми. Bash не является исключением из этого правила.
Это важно, потому что в Bash &&и ||имеют такой же приоритет.
Итак, что происходит в вашем примере, это то, что крайняя левая операция ( ||) выполняется первой:
true || echo aaa
Поскольку true, очевидно, это правда, ||оператор замыкает накоротко и весь оператор считается верным без необходимости оценивать, echo aaaкак вы ожидаете. Теперь осталось сделать самую правую операцию:
(...)&& echo bbb
Поскольку первая операция имеет значение true (т. Е. Имеет состояние выхода 0), это как если бы вы выполняли
true && echo bbb
так что &&не будет короткое замыкание, поэтому вы видите bbbотражение.
Вы бы получили такое же поведение с
false && echo aaa || echo bbb
Примечания на основе комментариев
Следует отметить, что правило левой ассоциативности соблюдается только в том случае, если оба оператора имеют одинаковый приоритет. Это не тот случай , когда вы используете эти операторы в сочетании с такими ключевыми словами, как [[...]]или ((...))или использовать -oи -aоператор в качестве аргументов testили [команд. В таких случаях AND ( &&или -a) имеет приоритет над OR ( ||или -o). Спасибо комментарию Стефана Чазеласа за разъяснение этого вопроса.
Похоже, что в C и C-подобных языках &&приоритет выше, чем, ||вероятно, поэтому вы ожидали, что ваша оригинальная конструкция будет вести себя как
true ||(echo aaa && echo bbb).
Однако это не относится к Bash, в котором оба оператора имеют одинаковый приоритет, поэтому Bash анализирует ваше выражение, используя правило левой ассоциативности. Спасибо за комментарий Кевина за то, что он поднял этот вопрос.
Также могут быть случаи, когда все 3 выражения оцениваются. Если первая команда возвращает ненулевой статус выхода, ||не будет короткого замыкания и продолжит выполнение второй команды. Если вторая команда возвращается с нулевым статусом выхода, то также &&не будет происходить короткое замыкание, и будет выполнена третья команда. Спасибо комментарию Игнасио Васкеса-Абрамса за то, что он поднял этот вопрос.
Крошечную записку , чтобы добавить немного больше путаницы: в то время как &&и ||операторы оболочки , как и в cmd1 && cmd2 || cmd3имеют одинаковый приоритет, то &&по прибытии ((...))и [[...]]имеет приоритет над ||( ((a || b && c))есть ((a || (b && c)))). То же самое касается -a/ -oв test/ [и findи &/ |в expr.
Стефан Шазелас
16
В с-подобных языках &&имеет более высокий приоритет, чем ||, поэтому ожидаемое поведение OP будет иметь место. Неосторожные пользователи, привыкшие к таким языкам, могут не осознавать, что в bash они имеют одинаковый приоритет, поэтому стоит более четко указать, что они имеют такой же приоритет в bash.
Кевин
Также обратите внимание, что существуют ситуации, когда все три команды могут быть выполнены, т.е. если средняя команда может вернуть либо true, либо false.
Сегодня для меня самый популярный запрос Google о «приоритете операторов bash» - это tldp.org/LDP/abs/html/opprecedence.html ... который утверждает, что && имеет более высокий приоритет, чем ||. Еще @JosephR. явно прав де-факто и де-юре тоже. Dash ведет себя так же и ищет «приоритет» в pubs.opengroup.org/onlinepubs/009695399/utilities/… Я вижу, что это требование POSIX, поэтому мы можем зависеть от него. Все, что я нашел для сообщения об ошибках, был адрес электронной почты автора, который, несомненно, был спамом за последние шесть лет. Я все равно попробую ...
Мартин Дори
62
Если вы хотите, чтобы несколько вещей зависели от вашего состояния, сгруппируйте их:
true ||{ echo aaa && echo bbb;}
Это ничего не печатает, в то время как
true &&{ echo aaa && echo bbb;}
печатает обе строки.
Причина, по которой это происходит, намного проще, чем это делает Джозеф. Помните, что Bash делает с ||и &&. Все дело в статусе возврата предыдущей команды. Буквальный взгляд на вашу необработанную команду:
( true || echo aaa )&& echo bbb
Первая команда ( true || echo aaa) завершается с 0.
+1 За достижение ожидаемого результата ОП. Обратите внимание, что круглые скобки, которые вы поместили, (true || echo aaa) && echo bbbименно то, что я делаю.
Джозеф Р.
1
Приятно видеть @Oli в этой части света.
Брайам
7
Обратите внимание на полуколонну ;в самом конце. Без этого не получится!
Серж Строобандт
2
У меня были проблемы с этим, потому что я пропускал конечную точку с запятой после последней команды (например, echo bbb;в первых примерах). Как только я понял, что мне не хватает этого, этот ответ был именно тем, что я искал. +1 за помощь в выяснении, как добиться того, чего я хотел!
Эти &&и ||операторы не точные встроенные замены для если-то-иначе. Хотя, если их использовать осторожно, они могут сделать почти то же самое.
Один тест прост и однозначен ...
[[ 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?)
Почему отражаются как ЛОЖЬ, так и ИСТИНА?
Здесь происходит то, что мы не осознали этого &&и ||являемся перегруженными операторами, которые действуют внутри скобок условных тестов иначе, [[ ]]чем в списках AND и OR (условное выполнение), которые мы здесь имеем.
Из справочной страницы bash (отредактировано) ...
Списки
Список - это последовательность из одного или нескольких конвейеров, разделенных одним из операторов;, &, && или ││ и, возможно, завершенных одним из;, &, или. Из этих операторов списка && и ││ имеют одинаковый приоритет, после чего следует; и &, которые имеют равный приоритет.
Последовательность из одного или нескольких символов новой строки может появиться в списке вместо точки с запятой для разделения команд.
Если команда завершается оператором управления &, оболочка выполняет команду в фоновом режиме в подоболочке. Оболочка не ожидает завершения команды, и возвращается статус 0. Команды, разделенные символом a; выполняются последовательно; оболочка ожидает завершения каждой команды по очереди. Статус возврата - это статус выхода последней выполненной команды.
Списки AND и OR являются последовательностями одного из нескольких конвейеров, разделенных управляющими операторами && и, соответственно. Списки AND и OR выполняются с левой ассоциативностью.
Список AND имеет вид ... command1 && command2
Command2 выполняется тогда и только тогда, когда command1 возвращает нулевое состояние выхода.
Список ИЛИ имеет форму ... 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
(i.e. 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, может быть понятнее что-то вроде следующего (фигурные скобки необходимы для группировки последних 2 команд) ...
[[ $1 ==--help ]]&&{ echo "$HELP"; exit;}
Множественные операторы &&и ||операторы, где каждая команда, кроме последней, является тестом (т. Е. Внутри скобок [[ ]]), обычно также безопасны, поскольку все, кроме последнего оператора, ведут себя так, как ожидается. Последний оператор действует больше как предложение thenили else.
Я также смутился из-за этого, но вот как я думаю о том, как Bash читает ваше утверждение (как оно читает символы слева направо):
Найдено символ true. Это нужно будет оценить, как только будет достигнут конец команды. На данный момент, не знаю, есть ли у него какие-либо аргументы. Сохраните команду в буфере выполнения.
Найдено символ ||. Предыдущая команда завершена, поэтому оцените ее. Команда (буфер) выполняется true. Результат оценки: 0 (т.е. успех). Сохранить результат 0 в регистре «последней оценки». Теперь рассмотрим ||сам символ . Это зависит от того, что результат последней оценки не равен нулю. Проверил регистр «последней оценки» и нашел 0. Поскольку 0 не является ненулевым, следующую команду оценивать не нужно.
Найдено символ echo. Может игнорировать этот символ, потому что следующую команду не нужно было оценивать.
Найдено символ aaa. Это аргумент команды echo(3), но, поскольку echo(3) оценивать не нужно, его можно игнорировать.
Найдено символ &&. Это зависит от того, что результат последней оценки равен нулю. Проверил регистр «последней оценки» и нашел 0. Поскольку 0 - ноль, необходимо выполнить следующую команду.
Найдено символ echo. Эта команда должна быть оценена, как только достигнут конец команды, потому что следующая команда должна была быть оценена. Сохраните команду в буфере выполнения.
Найдено символ bbb. Это аргумент команды echo(6). Так echoкак нужно было оценить, добавить bbbв буфер выполнения.
Достигнут конец строки. Предыдущая команда завершена, и ее необходимо оценить. Команда (буфер) выполняется echo bbb. Результат оценки: 0 (т.е. успех). Сохранить результат 0 в регистре «последней оценки».
И, конечно же, последний шаг вызывает bbbвывод на консоль.
&&
и||
операторы оболочки , как и вcmd1 && cmd2 || cmd3
имеют одинаковый приоритет, то&&
по прибытии((...))
и[[...]]
имеет приоритет над||
(((a || b && c))
есть((a || (b && c)))
). То же самое касается-a
/-o
вtest
/[
иfind
и&
/|
вexpr
.&&
имеет более высокий приоритет, чем||
, поэтому ожидаемое поведение OP будет иметь место. Неосторожные пользователи, привыкшие к таким языкам, могут не осознавать, что в bash они имеют одинаковый приоритет, поэтому стоит более четко указать, что они имеют такой же приоритет в bash.Если вы хотите, чтобы несколько вещей зависели от вашего состояния, сгруппируйте их:
Это ничего не печатает, в то время как
печатает обе строки.
Причина, по которой это происходит, намного проще, чем это делает Джозеф. Помните, что Bash делает с
||
и&&
. Все дело в статусе возврата предыдущей команды. Буквальный взгляд на вашу необработанную команду:Первая команда (
true || echo aaa
) завершается с0
.источник
(true || echo aaa) && echo bbb
именно то, что я делаю.;
в самом конце. Без этого не получится!echo bbb;
в первых примерах). Как только я понял, что мне не хватает этого, этот ответ был именно тем, что я искал. +1 за помощь в выяснении, как добиться того, чего я хотел!(...)
и{...}
обозначения: руководство по группированию командЭти
&&
и||
операторы не точные встроенные замены для если-то-иначе. Хотя, если их использовать осторожно, они могут сделать почти то же самое.Один тест прост и однозначен ...
Однако попытка добавить несколько тестов может привести к неожиданным результатам ...
Почему отражаются как ЛОЖЬ, так и ИСТИНА?
Здесь происходит то, что мы не осознали этого
&&
и||
являемся перегруженными операторами, которые действуют внутри скобок условных тестов иначе,[[ ]]
чем в списках AND и OR (условное выполнение), которые мы здесь имеем.Из справочной страницы bash (отредактировано) ...
Возвращаясь к нашему последнему примеру ...
Хорошо. Если это правильно, то почему следующий к последнему примеру что-нибудь эхо?
Риск логических ошибок при использовании более одной
&&
или||
в списке команд довольно высок.рекомендации
Один
&&
или||
в списке команд работает, как ожидалось, так что это довольно безопасно. Если это ситуация, когда вам не нужно выражение else, может быть понятнее что-то вроде следующего (фигурные скобки необходимы для группировки последних 2 команд) ...Множественные операторы
&&
и||
операторы, где каждая команда, кроме последней, является тестом (т. Е. Внутри скобок[[ ]]
), обычно также безопасны, поскольку все, кроме последнего оператора, ведут себя так, как ожидается. Последний оператор действует больше как предложениеthen
илиelse
.источник
Я также смутился из-за этого, но вот как я думаю о том, как Bash читает ваше утверждение (как оно читает символы слева направо):
true
. Это нужно будет оценить, как только будет достигнут конец команды. На данный момент, не знаю, есть ли у него какие-либо аргументы. Сохраните команду в буфере выполнения.||
. Предыдущая команда завершена, поэтому оцените ее. Команда (буфер) выполняетсяtrue
. Результат оценки: 0 (т.е. успех). Сохранить результат 0 в регистре «последней оценки». Теперь рассмотрим||
сам символ . Это зависит от того, что результат последней оценки не равен нулю. Проверил регистр «последней оценки» и нашел 0. Поскольку 0 не является ненулевым, следующую команду оценивать не нужно.echo
. Может игнорировать этот символ, потому что следующую команду не нужно было оценивать.aaa
. Это аргумент командыecho
(3), но, посколькуecho
(3) оценивать не нужно, его можно игнорировать.&&
. Это зависит от того, что результат последней оценки равен нулю. Проверил регистр «последней оценки» и нашел 0. Поскольку 0 - ноль, необходимо выполнить следующую команду.echo
. Эта команда должна быть оценена, как только достигнут конец команды, потому что следующая команда должна была быть оценена. Сохраните команду в буфере выполнения.bbb
. Это аргумент командыecho
(6). Такecho
как нужно было оценить, добавитьbbb
в буфер выполнения.echo bbb
. Результат оценки: 0 (т.е. успех). Сохранить результат 0 в регистре «последней оценки».И, конечно же, последний шаг вызывает
bbb
вывод на консоль.источник