Рассмотрим два условных выражения expr1
и expr2
, например, $i -eq $j
и $k -eq $l
. Мы можем написать это bash
несколькими способами. Вот две возможности
[[ expr1 || expr2 ]]
[[ expr1 ]] || [[ expr2 ]]
Я вполне уверен, что видел здесь рекомендации о том, что второе предпочтительнее, но я не могу найти доказательств, подтверждающих это.
Вот пример сценария, который, кажется, демонстрирует, что нет никакой разницы:
for i in 0 1
do
for j in 0 1
do
for k in 0 1
do
for l in 0 1
do
if [[ $i -eq $j || $k -eq $l ]]; then printf "1-yes\t"; else printf "1-no\t"; fi
if [[ $i -eq $j ]] || [[ $k -eq $l ]]; then printf "2-yes\n"; else printf "2-no\n"; fi
done
done
done
done
и вывод, показывающий, что обе условные конструкции дают одинаковый результат:
1-yes 2-yes
1-yes 2-yes
1-yes 2-yes
1-yes 2-yes
1-yes 2-yes
1-no 2-no
1-no 2-no
1-yes 2-yes
1-yes 2-yes
1-no 2-no
1-no 2-no
1-yes 2-yes
1-yes 2-yes
1-yes 2-yes
1-yes 2-yes
1-yes 2-yes
Есть ли польза от использования одной конструкции над другой?
Для бонусных баллов тот же вопрос, но обобщающий несколько условий, используя ||
и &&
. Например, [[ expr1 && expr2 || expr3 ]]
.
[
илиtest
( не[[
), найдитеOB
теги (связанные с «устаревшим» определением) в спецификации POSIXtest
. Вtest
, есть некоторые патологические случаи , когда это может быть невозможно сказать , если(
или)
предназначается , чтобы быть синтаксисом смысл команды испытаний или строки , которые будут проверены, так что люди , которые игнорируют маркеры устаревания и использовать этот синтаксис может фактически нужен otherwise- устаревшая"x$foo"
практика; с[[
, это не проблема.Ответы:
Я думаю, что рекомендация, которую вы видели, была для POSIX sh и / или
test
команды, которая удваивается как[
команда, а не как[[
конструкция, которая появилась в ksh (спасибо Stéphane Chazelas за подсказку), а также используется, например, в bash, zsh и некоторых других снаряды.В большинстве языков, таких как C, когда условие уже известно как истинное или ложное, нет необходимости оценивать оставшиеся части в зависимости от операции: если true не после логической или следующей за ней, если false не после логической и , и т.д. Это, конечно, позволяет, например, остановиться, когда указатель равен NULL, и не пытаться разыменовать его в следующем предложении.
Но ш «s
[ expr1 -o expr2 ]
конструкция (включая реализацию в Bash) не делает этого: он всегда оценивает обе стороны, когда один хотел бы только выражение1 быть оценены. Это могло быть сделано для совместимости сtest
реализацией команды. С другой стороны, sh||
и&&
действительно следуют обычному принципу: не оценивается, если это не изменит результат.Таким образом, разница в примечании будет:
что дает:
Выше каждый
[
мог быть заменен/usr/bin/[
на псевдонимtest
команды, который использовался до того, как[
был встроен в оболочку.Пока две следующие конструкции:
или
Будет только уступать
true
и оставлятьeffect
пустым:||
ведет себя правильно, и[[
исправил эту проблему тоже.ОБНОВИТЬ:
Как прокомментировал @ StéphaneChazelas, я пропустил несколько различий, связанных с первоначальным вопросом. Я просто поставлю здесь самый важный (по крайней мере для меня): приоритет операторов.
Пока оболочка не будет учитывать приоритет:
Выход (потому что нет приоритета и, следовательно, сначала
true || true
оценивается, а затем&& false
):внутри
[[ ]]
на&&
оператора имеет приоритет над||
:Урожай (потому что
1 -eq 1 && 1 -eq 0
он сгруппирован и, следовательно, является вторым членом||
):по крайней мере для кш , баш , зш .
Так
[[ ]]
улучшилось поведение как[ ]
логических операторов прямой, так и логической оболочки.источник
[ x -o y ]
не нужно оценивать обе стороны. В то время как GNUtest
или[
встроенный вbash
do, вы обнаружите,strace zsh -c '[ x -o -f /x ]'
что[
не пытается stat ()/x
. То же самое сmksh
. Но это правда , что-o
и-a
серьезно нарушена, не может быть использована надежно и устаревшим POSIX.[[...]]
, что внутри ,&&
имеет более высокий приоритет, чем||
, в то время как снаружи они имеют равный приоритет. Сравнитеksh -c '[[ x || x && "" ]]'
противsh -c 'true || true && false'
[
/test
утилита не имеет==
оператора. Оператор равенства=
(обратите внимание , что внутри КШ это[[...]]
,=
/==
не операторы равенства, но согласующий шаблон из них).&&
имеет приоритет над||
внутренним[[...]]
(или((...))
) , но не&&
и||
оболочками операторы.[[ x || y && z ]]
isx || (y && z)
whilex || y && z
is(x || y) && z
(операторы вычисляются слева направо без приоритета). Подобно тому, как*
имеет приоритет над+
(1 + 2 * 3
есть1 + (2 * 3)
), но+
и-
имеет такой же приоритет.&&
имеет приоритет над||
, то это должно бытьtrue || (true && false)
, вместо(true || true) && false