Разница между [[ ]] и [ ] или (( )) и ( ) в Bash

Разница между [[ ]] и [ ] или (( )) и ( ) в Bash

В чем разница между использованием [[ condition ]]and [ condition ]или (( condition ))and ( condition )? В каком случае нам нужно использовать любой из них?

  • (( 10 > 9 ))работает, но (( 10 -gt 9 ))не
  • [[ 10 -gt 9 ]]работает, но [[ 10 > 9 ]]не

решение1

((...))это оболочкаарифметикаконструкт. Операторы, которые вы можете использовать, описаны в руководстве:6.5 Арифметика оболочки

(...)этогруппировкаконструкция, которая выполняет содержащиеся команды в подоболочке:3.2.4.3 Группировка команд

[...]является "устаревшей" условной конструкцией. Документация находится на6.4 Условные выражения Bash

[[...]]делает все, что [...]делает. Разница в том, что разбиение слов и расширение глоба не выполняются для переменных внутри, [[...]]поэтому заключение переменных в кавычки не так важно. Кроме того, [[может делатьсопоставление с образцомс ==оператором ирегулярное выражение сопоставлениес =~оператором.

Причина, [[ 10 > 9 ]]по которой вы получаете неожиданный результат, заключается в том, что >оператор внутри [[...]]предназначен длясравнение строка строка «10» «меньше» строки «9».

решение2

О ((…))и(…)

Как указано вэтот другой ответ, ((…))является арифметической конструкцией оболочки (aбашизм) и (…)запускает команду(ы) в подоболочке. Они сильно отличаются друг от друга и от [ … ]или [[ … ]].

С другой стороны, [ … ]и [[ … ]]очень похожи в простых случаях; их можно спутать. Остальная часть этого ответа сосредоточена на [ … ]и [[ … ]].


Официальная записка

Мои цели здесь таковы:

  • чтобы подробно остановиться наразличия и сходства между [ … ]и[[ … ]];
  • предоставить канонический ответ в теме [ … ]vs [[ … ]], чтобы вопросы, на которые можно ответить с помощью « [is not [[», можно было бы связать здесь;
  • предоставить достаточно исчерпывающий ответ, чтобы пользователи больше не испытывали соблазна публиковать ответы, каждый из которых фокусируется на одном различии.

Что [?

  1. [проводит тест.

    Целью [является проверка чего-либо (например, существует ли какой-либо файл) и возврат кода выхода ноль, если проверка пройдена успешно, и ненулевой код в противном случае.

  2. [это команда.

    [— это имя команды, например catили echo. [(например echo) — встроенная команда в Bash, но это означает только то, что ее можно запустить, не порождая дополнительный процесс, она все равно ведет себя как команда. Вероятно, [в вашей системе есть автономный исполняемый файл (например /bin/[); попробуйте type -a [в Bash, чтобы узнать.

  3. [требуется пробел после.

    Как команда, [принимает аргументы. Как и в случае с любой другой командой, аргументы должны быть отделены друг от друга и от имени команды. [foo— это другое имя команды, оно не эквивалентно [ foo(аналогично и более очевидно, echoHello worldчто не является echo Hello world). Вам определенно нужен пробел (или табуляция) после [.

  4. [требует ].

    [требует ]в качестве последнего аргумента, иначе он отказывается работать. Это только для того, чтобы выделиться [ … ]как блок, как будто это какой-то особый синтаксис; но это не особый синтаксис. Чтобы передать ]в качестве последнего аргумента, вам нужен пробел (или табуляция) перед ним. Для сравнения: последний аргумент в [ … bar]— это , bar]который не является ], поэтому [ … bar]не является допустимой командой.

  5. [(почти) как test.

    [ … ]эквивалентно test …. Другими словами, вы можете преобразовать любую [команду в testкоманду, удалив ]и изменив имя команды. И вы можете преобразовать любую testкоманду в [команду, изменив имя команды и добавив ].

  6. [не является чем-то особенным.

    Хорошо помнить, [что это не что-то особенное, это просто несколько причудливое название для команды. Если вы это осознаете, то вас не удивит тот факт, чтовсесинтаксический анализ и обработка оболочкидоВыполнение команды ( testили echoи lsт.д.) также происходит, если команда [. В частности:

    • [не знает, заключили ли вы в кавычки какой-либо из его аргументов, он получает аргументы после того, как оболочка удаляет кавычки.

    • Без кавычек и без экранирования &&или ||между [и ]не интерпретируется как аргумент для [. Другими словами, [ … || … ]интерпретируется как [ …(недопустимо, поскольку нет ]) и … ](отдельная команда, какая бы она ни была) логически связанная с ||, точно так же, как command1 || command2.

  7. [определено POSIX.

    ЗдесьСпецификация POSIX для [/test. Можно позвонить [портативный. Примечания:

    • Некоторые основные являются расширениями, некоторые отмечены как устаревшие. Например, наш недействительный код ( [ … || … ]) можно исправить как [ … -o … ], но поскольку -oон устарел, лучшим исправлением будет [ … ] || [ … ].
    • Реализации могут поддерживать больше основных. [Встроенный в Bash поддерживает (см. help test); [двоичный файл (например /bin/[) в вашей ОС может (см. man test).

Что такое [[? Чем [[отличается от [?

Примечания:
- Вопрос помечен тегом, теперь мы говорим о[[ в Баше.
- Нумерация абзацев в этом разделе соответствует нумерации абзацев в предыдущем разделе.

  1. (сходство)[[проводит тест.

    Цель [[— проверить что-либо (например, существует ли какой-либо файл) и вернуть нулевой код выхода, если тест пройден успешно, и ненулевой в противном случае. [[может проверить что угодно [, может и многое другое.

  2. (разница)[[— ключевое слово.

    ​является[[ ключевым словом оболочки (вызовите type [[в Bash, чтобы подтвердить это). Как и [встроенное, [[ключевое слово принадлежит Bash; но ононевести себя как по команде.

  3. (сходство)[[требуется пробел после.

    (Или вкладка).

  4. (сходство)[[требует ]].

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

  5. (разница)Эквивалентной команды с обычным видом не существует [[.

    Нет другой команды/ключевого слова/чего-либо еще, что могло бы заменить [[так, как testможет заменить [.

  6. (разница)[[ являетсяособенный.

    [[изменяет способ, которым оболочка анализирует и интерпретирует код, вплоть до сопоставления ]]. Обычный анализ и обработка, выполняемые оболочкой перед запуском [или test(или и echoт ls. д.), не применяются внутри [[ … ]]. В частности:

    • [[ являетсязнать, если вы цитировали любой из его "аргументов". Двойные кавычки для переменных не нужны (хотя обычноэто), но в некоторых случаях заключение строки (или переменной в двойные кавычки) имеет значение (см.этот ответгде упоминается «правая сторона =или ==или !=или =~»).

    • &&или ||между [[и ]]принадлежит [[ … ]]конструкции. [[ … || … ]]может быть допустимым фрагментом кода, фактически эквивалентным [[ … ]] || [[ … ]].

  7. (разница)[[не определено в POSIX.

    ​непереносимо[[ . [[работает в Bash, не работает в pure sh. Другие оболочки могут поддерживать [[(например, Zsh), но они [[могут отличаться от [[Bash.

    [[не предназначен для соответствия спецификации [/ test. Во многих случаях замена [на [[и ]на ]]даст вам [[ … ]]фрагмент, эквивалентный исходному [ … ]фрагменту; но не всегда,иногдавнутренняя часть должна быть отрегулирована. Так что не меняйте [на [[слепо. Слепое изменение [[на [еще более рискованно, потому что есть тесты, [[которые можно сделать, а [нельзя.


Другие примечания:

  1. Ни то, [ни другое не [[является частью синтаксиса if … then …. Помните, ifпросто проверяет статус выхода некоторого кода. if true; then …является допустимым, if sleep 5; then …является допустимым, аналогично if [ … ]; then …или if [[ … ]]; then …является допустимым (если часть [ … ]or [[ … ]]сама является допустимым фрагментом).

    Поскольку ((…))or (…)также возвращает некоторый статус выхода (зависящий от того, что находится внутри), его можно использовать ifи после.

  2. Лично я предпочитаю использовать [для любого теста [, который можно легко сделать, прибегая к нему, [[когда это действительно необходимо. Таким образом, я не привыкаю к ​​непереносимому [[и знаю, что я могу сделать в оболочке, не такой богатой, как Bash.

  3. Исполняемый файл [в вашей ОС не обязательно строго эквивалентен [встроенному файлу в вашем Bash. Если [он вызывается Bash, то встроенный файл выполнит работу. Если [он вызывается чем-то другим, то исполняемый файл выполнит работу. Например, find … -exec [ … ] …использует исполняемый файл. Стандартного исполняемого файла нет [[(по крайней мере, в известных мне системах), поэтому find … -exec [[ … ]] …он никогда не будет работать. Если бы был [[исполняемый файл, то он должен был бы вести себя как команда, он не смог бы имитировать ключевое слово.

решение3

Между [ ] и [[ ]] существует огромная разница:

a=0;
if [ $a = 1 -a $(grep ERR 1.log|wc -l) -ne 0 ];then
      echo "error";
fi 

при использовании [ ], оператор and -a не поддерживает оценку Short-circuit. В приведенном выше коде даже первое условие возвращает false, второе условие все равно будет оценено. Если файл 1.log не существует, он выдаст сообщение "No such file or directory".

Но для [[ ]]:

a=0
if [[ $a = 1 && $(grep ERR 1.log|wc -l) -ne 0 ];then
    echo "error"
fi

даже если файл «1.log» не существует, это будет нормально, поскольку второе условие не будет оценено, поскольку первое условие ложно.

решение4

Я бы написал об этом в комментарии, но пока мне это не разрешено.

Одно из различий, которое я заметил между [] и [[]], заключается в том, что в первом случае можно использовать множественные сравнения.

То же самое сравнение вызывает синтаксическую ошибку в [[]]

a=1
b=2

Это работает:

$  [ "$a" -gt 0 -a "$b" -gt "$a" ] && { echo '[b>a>0]'; }
[b>a>0]

Это не работает:

$ [[ $a -gt 0 -a $b -gt $a ]] && { echo '[[b>a>0]]'; }
-bash: syntax error in conditional expression
-bash: syntax error near `-a'

Я не особо вникал, почему это так, но при использовании множественных сравнений я использую [].

Связанный контент