В чем разница между операторами Bash [[vs [vs (vs ((?)


248

Я немного запутался в том, что эти операторы делают по-разному при использовании в bash (скобки, двойные скобки, круглые скобки и двойные скобки).

[[ , [ , ( , ((

Я видел, как люди используют их, если такие заявления:

if [[condition]]

if [condition]

if ((condition))

if (condition)

4
Вы можете сначала посмотреть на unix.stackexchange.com/questions/tagged/test
cuonglm


3
@cuonglm Иронично, потому что эта ссылка дает этот вопрос в качестве первого результата. Парадокс!
Безумный

5
Я полагаю, что чтение документации не вариант?
Гонки легкости на орбите

34
Скобки и скобки не так легко найти в документации, и это все, что у вас есть, если вы не знаете названия этих функций.
ilkkachu

Ответы:


260

ifЗаявление обычно выглядит следующим образом

if commands1
then
   commands2
else
   commands3
fi

Предложение thenвыполняется, если код выхода commands1равен нулю. Если код выхода не равен нулю, то elseпредложение выполняется. commands1может быть простым или сложным. Он может, например, представлять собой последовательность из одного или нескольких конвейеров , разделенных одним из операторов ;, &, &&, или ||. Эти ifусловия , приведенные ниже , являются лишь частными случаями commands1:

  1. if [ condition ]

    Это традиционная testкоманда оболочки . Он доступен на всех оболочках POSIX. Команда test устанавливает код выхода, и ifоператор действует соответственно. Типичные тесты - существуют ли файл или одно число равно другому.

  2. if [[ condition ]]

    Это новая модернизированы вариация testиз KSH , что колотить и ЗШ также поддержки. Эта testкоманда также устанавливает код выхода, и ifоператор действует соответственно. Среди его расширенных возможностей он может проверить, соответствует ли строка регулярному выражению.

  3. if ((condition))

    Еще одно расширение ksh, которое также поддерживают bash и zsh . Это выполняет арифметику. В результате арифметики устанавливается код выхода и ifоператор действует соответствующим образом. Он возвращает код выхода, равный нулю (true), если результат арифметического вычисления отличен от нуля. Мол [[...]], эта форма не POSIX и, следовательно, не переносимая.

  4. if (command)

    Это запускает команду в подоболочке. Когда команда завершается, она устанавливает код выхода и ifоператор действует соответственно.

    Типичная причина для использования подоболочки подобного типа заключается в том, чтобы ограничить побочные эффекты commandпри commandнеобходимости назначения переменных или других изменений в среде оболочки. Такие изменения не сохраняются после завершения подоболочки.

  5. if command

    Команда выполняется, и ifоператор действует в соответствии с кодом выхода.


24
Спасибо за включение 5-го варианта. Это ключ к пониманию того, как это на самом деле работает и на удивление недостаточно используется.
цыплята

4
Обратите внимание, что [на самом деле это двоичный файл, а не внутренняя команда или символ. Как правило, живет в /bin.
Жюльен Р.

8
@JulienR. на самом деле [является встроенным, как есть test. Для совместимости доступны двоичные версии. Проверьте help [и help test.
OldTimer 20.09.16

4
Стоит отметить, что while ((не POSIX, $((т.е. арифметическое расширение есть, и их легко спутать. Часто обходной путь - использовать что-то вроде [ $((2+2)) -eq 4 ]использования арифметики в условных выражениях
Сергей Колодяжный,

1
Я хотел бы проголосовать за этот ответ более одного раза. Идеальное объяснение.
Энтони Гатлин,

77
  • (…)круглые скобки обозначают подоболочку . То, что внутри них, не является выражением, как во многих других языках. Это список команд (как и вне скобок). Эти команды выполняются в отдельном подпроцессе, поэтому любое перенаправление, присваивание и т. Д., Выполняемые внутри скобок, не влияют за пределы скобок.
    • С ведущим знаком доллара $(…)- подстановка команды : в скобках есть команда, а вывод команды используется как часть командной строки (после дополнительных расширений, если подстановка не заключена в двойные кавычки, но это уже другая история ) ,
  • { … }Скобки подобны скобкам в том смысле, что они группируют команды, но они влияют только на синтаксический анализ, а не на группировку. Программа x=2; { x=4; }; echo $xпечатает 4, тогда как x=2; (x=4); echo $xпечатает 2. (Также скобки, являющиеся ключевыми словами, должны быть разделены и найдены в позиции команды (отсюда и пробел после {и ;перед }), тогда как скобки - нет. Это просто синтаксическая причуда.)
    • С ведущим знаком доллара, ${VAR}это расширение параметра , расширяющееся до значения переменной, с возможными дополнительными преобразованиями. ksh93Оболочка также поддерживает ${ cmd;}как форму подстановки команд , которые не порождают подоболочку.
  • ((…))двойные скобки окружают арифметическую инструкцию , то есть вычисление на целых числах, с синтаксисом, похожим на другие языки программирования. Этот синтаксис в основном используется для назначений и в условных выражениях. Это существует только в ksh / bash / zsh, а не в простой sh.
    • Тот же синтаксис используется в арифметических выражениях $((…)), которые расширяются до целочисленного значения выражения.
  • [ … ]одиночные скобки окружают условные выражения . Условные выражения в основном построены на операторах, таких как -n "$variable"проверка, является ли переменная пустой, и -e "$file"проверка, существует ли файл. Обратите внимание, что вам нужен пробел вокруг каждого оператора (например [ "$x" = "$y" ], нет [ "$x"="$y" ]) и пробел или символ, как ;внутри, так и вне скобок (например [ -n "$foo" ], нет [-n "$foo"]).
  • [[ … ]]двойные скобки - это альтернативная форма условных выражений в ksh / bash / zsh с несколькими дополнительными функциями, например, вы можете написать, [[ -L $file && -f $file ]]чтобы проверить, является ли файл символической ссылкой на обычный файл, тогда как для одиночных скобок требуется [ -L "$file" ] && [ -f "$file" ]. См. Почему расширение параметров с пробелами без кавычек работает в двойных скобках [[но не в одиночных скобках [? больше на эту тему.

В оболочке каждая команда является условной командой: каждая команда имеет статус возврата, который равен либо 0, что указывает на успех, либо целому числу от 1 до 255 (и, возможно, больше в некоторых оболочках), указывающему на сбой. Команда [ … ](или [[ … ]]синтаксическая форма) - это конкретная команда, которая также может быть написана test …и успешно выполняется, когда файл существует, или когда строка не пуста, или когда число меньше другого, и т. Д. ((…))Синтаксическая форма успешно выполняется, когда число ненулевой Вот несколько примеров условных выражений в сценарии оболочки:

  • Проверьте, myfileсодержит ли строка hello:

    if grep -q hello myfile; then 
  • Если mydirэто каталог, измените его и сделайте вещи:

    if cd mydir; then
      echo "Creating mydir/myfile"
      echo 'some content' >myfile
    else
      echo >&2 "Fatal error. This script requires mydir to exist."
    fi
  • Проверьте, есть ли файл, вызываемый myfileв текущем каталоге:

    if [ -e myfile ]; then 
  • То же самое, но в том числе висячие символические ссылки:

    if [ -e myfile ] || [ -L myfile ]; then 
  • Проверьте, является ли значение x(которое предполагается числовым) не менее 2, переносимо:

    if [ "$x" -ge 2 ]; then 
  • Проверьте, является ли значение x(которое предполагается числовым) не менее 2 в bash / ksh / zsh:

    if ((x >= 2)); then 

Обратите внимание, что одиночная скобка поддерживает -aвместо &&, поэтому можно написать:, [ -L $file -a -f $file ]что является одинаковым количеством символов в скобках без лишних [и ]...
Алексис Уилке

6
@AlexisWilke Операторы -aи -oявляются проблемными, потому что они могут привести к неправильным анализам, если некоторые из задействованных операндов выглядят как операторы. Вот почему я не упоминаю их: они имеют нулевое преимущество и не всегда работают. И никогда не пишите расширения переменных без кавычек без веской причины: [[ -L $file -a -f $file ]]это хорошо, но вам нужны одиночные скобки [ -L "$file" -a -f "$file" ](что нормально, например, если $fileвсегда начинается с /или ./).
Жиль

Обратите внимание, что это [[ -L $file && -f $file ]](нет -aс [[...]]вариантом).
Стефан

18

Из документации bash :

(list)Список выполняется в среде оболочки (см. ниже КОМАНДА ИСПОЛНИТЕЛЬНОЙ СРЕДЫ). Переменные и встроенные команды, которые влияют на среду оболочки, не остаются в силе после ее завершения. Статус возврата - это статус выхода из списка.

Другими словами, вы должны убедиться, что все, что происходит в 'list' (например, a cd), не имеет никакого эффекта вне (и ). Единственное, что утечет, это код завершения последней команды или set -eпервой команды, которая выдает ошибку (кроме нескольких, таких как if, whileи т. Д.)

((expression))Выражение оценивается в соответствии с правилами, описанными ниже в разделе АРИФМЕТИЧЕСКАЯ ОЦЕНКА. Если значение выражения не равно нулю, возвращаемое состояние равно 0; в противном случае возвращаемое состояние равно 1. Это в точности эквивалентно пустому выражению.

Это расширение bash, позволяющее вам выполнять математику. Это несколько похоже на использование exprбез всех ограничений expr(таких как наличие пробелов везде, экранирование *и т. Д.)

[[ expression ]]Вернуть состояние 0 или 1 в зависимости от оценки выражения условного выражения. Выражения состоят из основных цветов, описанных ниже под условными выражениями. Разделение слов и расширение пути не выполняются над словами между [[и]]; Выполняется расширение тильды, расширение параметров и переменных, арифметическое расширение, подстановка команд, подстановка процессов и удаление кавычек. Условные операторы, такие как -f, должны быть заключены в кавычки, чтобы их можно было определить в качестве основных.

При использовании с [[, операторы <и> сортируются лексикографически с использованием текущей локали.

Это предлагает расширенный тест для сравнения строк, чисел и файлов, немного похожий на testпредложения, но более мощный.

[ expr ]Вернуть состояние 0 (true) или 1 (false) в зависимости от оценки условного выражения expr. Каждый оператор и оператор и должен быть отдельным аргументом. Выражения состоят из основных цветов, описанных выше под условными выражениями. test не принимает никаких опций, а также не принимает и игнорирует аргумент - как означающий конец опций.

[...]

Этот звонит test. На самом деле, в старые времена, [была символическая ссылка на test. Это работает так же, и у вас есть те же ограничения. Поскольку двоичный файл знает имя, под которым он был запущен, тестовая программа может анализировать параметры, пока не найдет параметр ]. Приколы Unix.

Обратите внимание, что в случае bash, [и testявляются встроенными функциями (как упомянуто в комментарии), все же в значительной степени применяются те же ограничения.


1
Хотя testи [, конечно , встроенные команды в Bash, но это, вероятно , что внешний двоичный тоже существует.
ilkkachu

1
Внешний бинарный файл for [не является символической ссылкой testна большинство современных систем.
Random832

1
Каким-то образом я нахожу забавным, что они пытаются создать два отдельных двоичных файла, которые оба имеют именно то, что им нужно, вместо того, чтобы просто объединить их и добавить пару условных выражений. Хотя на самом деле strings /usr/bin/testпоказывает, что он также имеет текст справки, поэтому я не знаю, что сказать.
ilkkachu

2
@ Random832 Я понял вашу точку зрения об обосновании GNU, чтобы избежать неожиданного поведения arg0, но о требованиях POSIX, я бы не стал настолько утвердительным. В то время как стандарт, testочевидно, требует, чтобы команда существовала как отдельная файловая команда, ничто в ней не говорит о том, что ее [вариант также должен быть реализован таким образом. Например, Solaris 11 не предоставляет никакого [исполняемого файла, но, тем не менее, полностью соответствует стандартам POSIX
jlliagre

2
(выход 1) действует вне скобок.
Александр

14

[ против [[

Этот ответ будет охватывать [против [[подмножества вопроса.

Некоторые отличия в Bash 4.3.11:

  • Расширение POSIX против Bash:

  • обычная команда против магии

    • [ это обычная команда со странным именем.

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

      Ubuntu 16.04 на самом деле имеет исполняемый файл для него, /usr/bin/[предоставляемый coreutils, но встроенная версия bash имеет преимущество.

      Ничто не изменяется в том, как Bash анализирует команду.

      В частности, <происходит перенаправление &&и ||объединение нескольких команд, ( )генерируются подоболочки, если не выполняется экранирование \, и расширение слов происходит как обычно.

    • [[ X ]]это единственная конструкция, которая делает Xбыть проанализированным магическим образом. <, &&, ||И ()рассматриваются специально, и правила разбиения на слова различны.

      Есть и другие отличия, как =и =~.

      В Bashese: [это встроенная команда и [[ключевое слово: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && а также ||

    • [[ a = a && b = b ]]: правда, логично и
    • [ a = a && b = b ]: синтаксическая ошибка, &&проанализированная как разделитель команд ANDcmd1 && cmd2
    • [ a = a -a b = b ]: эквивалентно, но не рекомендуется POSIX³
    • [ a = a ] && [ b = b ]: POSIX и надежный аналог
  • (

    • [[ (a = a || a = b) && a = b ]]: ложный
    • [ ( a = a ) ]: синтаксическая ошибка, ()интерпретируется как подоболочка
    • [ \( a = a -o a = b \) -a a = b ]: эквивалентно, но ()не поддерживается POSIX
    • { [ a = a ] || [ a = b ]; } && [ a = b ]POSIX эквивалент 5
  • разделение слов и генерация имени файла при расширениях (split + glob)

    • x='a b'; [[ $x = 'a b' ]]: правда, цитаты не нужны
    • x='a b'; [ $x = 'a b' ]: синтаксическая ошибка, расширяется до [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: синтаксическая ошибка, если в текущем каталоге более одного файла.
    • x='a b'; [ "$x" = 'a b' ]: POSIX эквивалент
  • =

    • [[ ab = a? ]]: true, потому что он выполняет сопоставление с образцом ( * ? [это волшебство). Не расширяется до файлов в текущем каталоге.
    • [ ab = a? ]: a?glob расширяется. Так может быть true или false в зависимости от файлов в текущем каталоге.
    • [ ab = a\? ]: false, не глобальное расширение
    • =и ==одинаковы в обоих [и [[, но ==это расширение Bash.
    • case ab in (a?) echo match; esac: POSIX эквивалент
    • [[ ab =~ 'ab?' ]]: false 4 , теряет магию с''
    • [[ ab? =~ 'ab?' ]]: правда
  • =~

    • [[ ab =~ ab? ]]: true, POSIX расширенное совпадение с регулярным выражением , ?не расширяется
    • [ a =~ a ]: ошибка синтаксиса. Нет эквивалента Bash.
    • printf 'ab\n' | grep -Eq 'ab?': Эквивалент POSIX (только однострочные данные)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': Эквивалент POSIX.

Рекомендация : всегда используйте [].

Есть POSIX-эквиваленты для каждой [[ ]]конструкции, которую я видел.

Если вы используете [[ ]]вас:

  • потерять мобильность
  • Заставьте читателя изучить тонкости другого расширения bash. [это обычная команда со странным именем, никакой особой семантики не требуется.

¹ Вдохновленный эквивалентной [[...]]конструкции в оболочке Korn

² но терпит неудачу для некоторых значений aили b(например, +или index) и выполняет числовое сравнение, если aи bвыглядит как десятичные целые числа. expr "x$a" '<' "x$b"работает вокруг обоих.

³, а также терпит неудачу для некоторых значений aили bкак !или (.

4 в bash 3.2 и выше и при условии, что совместимость с bash 3.1 не включена (как с BASH_COMPAT=3.1)

5 , хотя группировка (здесь с {...;}командой группой вместо (...)которой будет запускать ненужную подоболочку) не является необходимой , как ||и &&операторы оболочки (в отличие от ||и && [[...]]операторов или -o/ -a [операторов) имеют одинаковый приоритет. Так [ a = a ] || [ a = b ] && [ a = b ]было бы эквивалентно.


@ StéphaneChazelas спасибо за информацию! Я добавил exprк ответу. Термин «расширение Bash» не подразумевает, что Bash был первой оболочкой, добавившей некоторый синтаксис, изучения POSIX sh против Bash уже достаточно, чтобы свести меня с ума.
Сиро Сантилли 新疆 at 中心 法轮功 六四 事件

Посмотрим man test, попробовал ли ты man [и потерялся. Это объяснит вариант POSIX.
Джонатан Комар

13

Некоторые примеры:

Традиционный тест:

foo="some thing"
# check if value of foo is not empty
if [ -n "$foo" ] ; then... 
if test -n "$foo" ; then... 

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

Тест нового стиля

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

if [[ -n $foo ]] ; then... 

Некоторая документация [и [[здесь .

Арифметический тест:

foo=12 bar=3
if (( $foo + $bar == 15 )) ; then ...  

«Нормальные» команды:

Все вышеперечисленное действует как обычные команды и ifможет принимать любую команду:

# grep returns true if it finds something
if grep pattern file ; then ...

Несколько команд:

Или мы можем использовать несколько команд. Обтекание набора команд ( ... )запускает их в подоболочке, создавая временную копию состояния оболочки (рабочий каталог, переменные). Если нам нужно временно запустить какую-то программу в другом каталоге:

# this will move to $somedir only for the duration of the subshell 
if ( cd $somedir ; some_test ) ; then ...

# while here, the rest of the script will see the new working
# directory, even after the test
if cd $somedir ; some_test ; then ...

1

Группировка Команд

Bash предоставляет два способа сгруппировать список команд, которые будут выполняться как единое целое.

( list )Размещение списка команд в скобках приводит к созданию среды подоболочки, и каждая из команд в списке выполняется в этой подоболочке. Поскольку список выполняется в подоболочке, назначения переменных не остаются в силе после завершения подоболочки.

$ a=1; (a=2; echo "inside: a=$a"); echo "outside: a=$a"
inside: a=2
outside: a=1

{ list; }Помещение списка команд в фигурные скобки приводит к тому, что список выполняется в текущем контексте оболочки . Никакая подоболочка не создана. Точка с запятой (или новая строка) следующий список обязателен. Источник

${} Parameter expansion Ex:  ANIMAL=duck; echo One $ANIMAL, two ${ANIMAL}s
$() Command substitution Ex: result=$(COMMAND) 
$(()) Arithmetic expansion Ex: var=$(( 20 + 5 )) 

Условные конструкции

Одинарная скобка, т. Е.[]
Для сравнения ==, !=, <,и >должна использоваться и для числового сравнения eq, ne,ltи gtдолжна использоваться.

Расширенные скобки т.е.[[]]

Во всех приведенных выше примерах мы использовали только одиночные скобки для заключения условного выражения, но bash допускает двойные скобки, что служит расширенной версией синтаксиса с одиночными скобками.

Для сравнения ==, !=, <,и >можно использовать буквально.

  • [это синоним тестовой команды. Даже если он встроен в оболочку, он создает новый процесс.
  • [[ это новая улучшенная версия, которая является ключевым словом, а не программой.
  • [[понимается Kornи Bash.

Источник

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.