9

Преимущественны ли двойные квадратные скобки [[ ]] над одинарными [ ] в Bash?

3

Недавно мой коллега заявил на код-ревью, что конструкция [[ ]] предпочтительнее, чем [ ] в таких конструкциях, как приведена ниже:

if [ "`id -nu`" = "$someuser" ] ; then
     echo "I love you madly, $someuser"
fi

Однако он не смог предоставить обоснование своему утверждению. Есть ли какие-либо причины использовать [[ ]] вместо [ ]?

3 ответ(ов)

0

В вопросе, отмеченном тегом 'bash' и в заголовке которого явно указано "в Bash", я немного удивлён количеством ответов, говорящих о том, что следует избегать [[...]], потому что это работает только в bash!

Действительно, основной причиной для отказа от использования [[...]] является портативность: если вы хотите написать сценарий, который будет работать в оболочках, совместимых с Bourne, даже если это не bash (или ksh, или zsh), вам стоит избегать [[...]]. Если вы находитесь в такой ситуации и хотите тестировать свои сценарии в более строго POSIX-совместимой оболочке, я рекомендую dash; хотя она не является строго POSIX (ей не хватает поддержки интернационализации, требуемой стандартом, и она поддерживает несколько непризнаваемых POSIX вещей, таких как локальные переменные), она значительно ближе, чем любая из трио bash/ksh/zsh.

Другим возражением, которое я вижу, по крайней мере в пределах предположения о том, что мы используем bash, является то, что [[...]] имеет свои особые правила, которые нужно изучить, в то время как [...] ведёт себя как просто ещё одна команда. Это снова верно (и мистер Сантилли привёл аргументы, показывающие все различия), но довольно субъективно, являются ли эти различия хорошими или плохими. Лично мне кажется освобождающим, что конструкция с двойными скобками позволяет использовать (...) для группировки, && и || для логики, < и > для сравнения, а также не заключённые в кавычки расширения параметров. Это как свой маленький замкнутый мир, в котором выражения работают больше как в традиционных языках программирования, не относящихся к командной оболочке.

Пункт, который я не видел, чтобы поднимался, заключается в том, что это поведение [[...]] совершенно согласуется с конструкцией арифметического расширения $((...)), которая является специфицированной POSIX и также позволяет использовать не заключённые в кавычки скобки, а также булевые и неравенства операторы (которые здесь выполняют числовые, а не лексические сравнения). По сути, каждый раз, когда вы видите двойные скобки, вы получаете тот же эффект защиты от кавычек.

(Bash и его современные варианты также используют ((...)) – без ведущего $ – как заголовок цикла в стиле C или как среду для выполнения арифметических операций без подстановки конечного значения; ни один из синтаксисов не является частью POSIX.)

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

0

Типичная ситуация, когда вы не можете использовать [[, возникает в скрипте configure.ac для autotools. В этом случае скобки имеют специальное и отличное значение, поэтому вам придется использовать test вместо [ или [[. Учтите, что test и [ - это одна и та же программа.

0

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

$ # Для строк, которые представляют собой целые числа, [ и [[ ведут себя одинаково
$ 
$ n=5 # присваиваем n строку, представляющую целое число
$ [[ $n -ge 0 ]] && printf "\t> $n неотрицательное\n"
        > 5 неотрицательное
$ [ "$n" -ge 0 ] && printf "\t> $n неотрицательное\n"
        > 5 неотрицательное
$ 
$ # Но поведение различается в нескольких случаях:
$ n=something # присваиваем n некорректную строку
$ [ "$n" -ge 0 ] && printf "\t> $n неотрицательное\n"
-bash: [: something: ожидается целочисленное выражение
$ [[ $n -ge 0 ]] && printf "\t> $n неотрицательное\n"
        > что-то неотрицательное
$ n=5foo
$ [[ $n -ge 0 ]] && printf "\t> $n неотрицательное\n"
-bash: 5foo: значение слишком велико для основания (ошибочный токен "5foo")

Чтобы проиллюстрировать непоследовательность поведения [[, рассмотрим следующее:

$ for n in 5foo 5.2 something; do [[ $n -ge 0 ]] && echo ok; done
-bash: 5foo: значение слишком велико для основания (ошибочный токен "5foo")
-bash: 5.2: синтаксическая ошибка: недопустимый арифметический оператор (ошибочный токен ".2")
ok
$ for n in 5foo 5.2 something; do [ "$n" -ge 0 ] && echo ok; done
-bash: [: 5foo: ожидается целочисленное выражение
-bash: [: 5.2: ожидается целочисленное выражение
-bash: [: something: ожидается целочисленное выражение

Интерпретация некоторых некорректных строк как нуля и непоследовательные сообщения об ошибках (или полное отсутствие сообщения об ошибке!) при использовании [[ $n -ge 0 ]], если $n не является допустимым целым числом, делает использование [[ небезопасным для числовых сравнений. По этой причине я настоятельно не рекомендую использовать [[ для числовых сравнений.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь