В чем разница между использованием [[ 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[[
», можно было бы связать здесь; - предоставить достаточно исчерпывающий ответ, чтобы пользователи больше не испытывали соблазна публиковать ответы, каждый из которых фокусируется на одном различии.
Что [
?
[
проводит тест.Целью
[
является проверка чего-либо (например, существует ли какой-либо файл) и возврат кода выхода ноль, если проверка пройдена успешно, и ненулевой код в противном случае.[
это команда.[
— это имя команды, напримерcat
илиecho
.[
(напримерecho
) — встроенная команда в Bash, но это означает только то, что ее можно запустить, не порождая дополнительный процесс, она все равно ведет себя как команда. Вероятно,[
в вашей системе есть автономный исполняемый файл (например/bin/[
); попробуйтеtype -a [
в Bash, чтобы узнать.[
требуется пробел после.Как команда,
[
принимает аргументы. Как и в случае с любой другой командой, аргументы должны быть отделены друг от друга и от имени команды.[foo
— это другое имя команды, оно не эквивалентно[ foo
(аналогично и более очевидно,echoHello world
что не являетсяecho Hello world
). Вам определенно нужен пробел (или табуляция) после[
.[
требует]
.[
требует]
в качестве последнего аргумента, иначе он отказывается работать. Это только для того, чтобы выделиться[ … ]
как блок, как будто это какой-то особый синтаксис; но это не особый синтаксис. Чтобы передать]
в качестве последнего аргумента, вам нужен пробел (или табуляция) перед ним. Для сравнения: последний аргумент в[ … bar]
— это ,bar]
который не является]
, поэтому[ … bar]
не является допустимой командой.[
(почти) какtest
.[ … ]
эквивалентноtest …
. Другими словами, вы можете преобразовать любую[
команду вtest
команду, удалив]
и изменив имя команды. И вы можете преобразовать любуюtest
команду в[
команду, изменив имя команды и добавив]
.[
не является чем-то особенным.Хорошо помнить,
[
что это не что-то особенное, это просто несколько причудливое название для команды. Если вы это осознаете, то вас не удивит тот факт, чтовсесинтаксический анализ и обработка оболочкидоВыполнение команды (test
илиecho
иls
т.д.) также происходит, если команда[
. В частности:[
не знает, заключили ли вы в кавычки какой-либо из его аргументов, он получает аргументы после того, как оболочка удаляет кавычки.Без кавычек и без экранирования
&&
или||
между[
и]
не интерпретируется как аргумент для[
. Другими словами,[ … || … ]
интерпретируется как[ …
(недопустимо, поскольку нет]
) и… ]
(отдельная команда, какая бы она ни…
была) логически связанная с||
, точно так же, какcommand1 || command2
.
[
определено POSIX.ЗдесьСпецификация POSIX для
[
/test
. Можно позвонить[
портативный. Примечания:- Некоторые основные являются расширениями, некоторые отмечены как устаревшие. Например, наш недействительный код (
[ … || … ]
) можно исправить как[ … -o … ]
, но поскольку-o
он устарел, лучшим исправлением будет[ … ] || [ … ]
. - Реализации могут поддерживать больше основных.
[
Встроенный в Bash поддерживает (см.help test
);[
двоичный файл (например/bin/[
) в вашей ОС может (см.man test
).
- Некоторые основные являются расширениями, некоторые отмечены как устаревшие. Например, наш недействительный код (
Что такое [[
? Чем [[
отличается от [
?
Примечания:
- Вопрос помечен тегомБаш, теперь мы говорим о[[
в Баше.
- Нумерация абзацев в этом разделе соответствует нумерации абзацев в предыдущем разделе.
(сходство)
[[
проводит тест.Цель
[[
— проверить что-либо (например, существует ли какой-либо файл) и вернуть нулевой код выхода, если тест пройден успешно, и ненулевой в противном случае.[[
может проверить что угодно[
, может и многое другое.(разница)
[[
— ключевое слово.является
[[
ключевым словом оболочки (вызовитеtype [[
в Bash, чтобы подтвердить это). Как и[
встроенное,[[
ключевое слово принадлежит Bash; но ононевести себя как по команде.(сходство)
[[
требуется пробел после.(Или вкладка).
(сходство)
[[
требует]]
.[[
требуется]]
позже в коде, однако это не только для того, чтобы[[ … ]]
выделиться как блок. Это особый синтаксис (разница, мы доберемся до нее). Вам нужен пробел (или табуляция) перед]]
.(разница)Эквивалентной команды с обычным видом не существует
[[
.Нет другой команды/ключевого слова/чего-либо еще, что могло бы заменить
[[
так, какtest
может заменить[
.(разница)
[[
являетсяособенный.[[
изменяет способ, которым оболочка анализирует и интерпретирует код, вплоть до сопоставления]]
. Обычный анализ и обработка, выполняемые оболочкой перед запуском[
илиtest
(или иecho
тls
. д.), не применяются внутри[[ … ]]
. В частности:[[
являетсязнать, если вы цитировали любой из его "аргументов". Двойные кавычки для переменных не нужны (хотя обычноэто), но в некоторых случаях заключение строки (или переменной в двойные кавычки) имеет значение (см.этот ответгде упоминается «правая сторона=
или==
или!=
или=~
»).&&
или||
между[[
и]]
принадлежит[[ … ]]
конструкции.[[ … || … ]]
может быть допустимым фрагментом кода, фактически эквивалентным[[ … ]] || [[ … ]]
.
(разница)
[[
не определено в POSIX.непереносимо
[[
.[[
работает в Bash, не работает в puresh
. Другие оболочки могут поддерживать[[
(например, Zsh), но они[[
могут отличаться от[[
Bash.[[
не предназначен для соответствия спецификации[
/test
. Во многих случаях замена[
на[[
и]
на]]
даст вам[[ … ]]
фрагмент, эквивалентный исходному[ … ]
фрагменту; но не всегда,иногдавнутренняя часть должна быть отрегулирована. Так что не меняйте[
на[[
слепо. Слепое изменение[[
на[
еще более рискованно, потому что есть тесты,[[
которые можно сделать, а[
нельзя.
Другие примечания:
Ни то,
[
ни другое не[[
является частью синтаксисаif … then …
. Помните,if
просто проверяет статус выхода некоторого кода.if true; then …
является допустимым,if sleep 5; then …
является допустимым, аналогичноif [ … ]; then …
илиif [[ … ]]; then …
является допустимым (если часть[ … ]
or[[ … ]]
сама является допустимым фрагментом).Поскольку
((…))
or(…)
также возвращает некоторый статус выхода (зависящий от того, что находится внутри), его можно использоватьif
и после.Лично я предпочитаю использовать
[
для любого теста[
, который можно легко сделать, прибегая к нему,[[
когда это действительно необходимо. Таким образом, я не привыкаю к непереносимому[[
и знаю, что я могу сделать в оболочке, не такой богатой, как Bash.Исполняемый файл
[
в вашей ОС не обязательно строго эквивалентен[
встроенному файлу в вашем 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'
Я не особо вникал, почему это так, но при использовании множественных сравнений я использую [].