Яка різниця між операторами Bash [[vs [vs (((?


245

Я трохи заплутаний у тому, що роблять ці оператори по-різному, коли вони використовуються в bash (дужки, подвійні дужки, дужки та подвійні дужки).

[[ , [ , ( , ((

Я бачив, як люди використовують їх, якщо такі заяви:

if [[condition]]

if [condition]

if ((condition))

if (condition)

4
Ви можете подивитися на unix.stackexchange.com/questions/tagged/test першим
cuonglm


3
@cuonglm Іронічно, оскільки це посилання дає це питання як перший результат. Парадокс!
Божевільний

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

34
Парентези та дужки не так просто шукати в документації, і це все, що ви маєте, якщо не знаєте назви цих функцій.
ilkkachu

Відповіді:


257

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, яка також підтримує bash та zsh . Ця 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

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

Як весело провести час друку нових ліній: if echo; then echo; fi.
AnthonyD973

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 ]що є однаковою кількістю символів у дужках без зайвих [і ]...
Alexis Wilke

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

Зауважте, що це [[ -L $file && -f $file ]](ні -aз [[...]]варіантом).
Стефан Шазелас

18

З документації bash :

(list)список виконується в середовищі додаткової оболонки (див. КОМАНДУ ВИКОНАВЧОГО ЕКОЛОГІЇ нижче). Змінні призначення та вбудовані команди, які впливають на середовище оболонки, не залишаються в силі після завершення команди. Статус повернення - це статус виходу зі списку.

Іншими словами, ви переконуєтесь, що те, що трапляється у "списку" (як-от cd), не має ефекту поза межами (та ). Єдине , що буде текти це код виходу останньої команди або з set -eпершою командою , яка генерує помилку (крім деяких , таких як if, whileі т.д.)

((expression))Вираз оцінюється за правилами, описаними нижче в АРИТМЕТИЧНІЙ ОЦІНКІ. Якщо значення виразу не дорівнює нулю, стан повернення дорівнює 0; інакше статус повернення дорівнює 1. Це абсолютно рівнозначно вираженню "вираз".

Це розширення на баш, що дозволяє займатися математикою. Це дещо схоже на використання exprбез усіх обмежень expr(наприклад, пробіли скрізь, втеча *тощо)

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

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

Це пропонує розширений тест для порівняння рядків, чисел та файлів, схожих на testпропозиції, але більш потужних.

[ expr ]Поверніть статус 0 (істинний) або 1 (хибний) залежно від оцінки умовного виразу expr. Кожен оператор і оператор і повинен бути окремим аргументом. Вирази складаються з праймериз, описаних вище в CONDITIONAL EXPRESSIONS. тест не приймає жодних варіантів, а також не приймає та ігнорує аргумент - як означає кінець параметрів.

[...]

Цей дзвонить test. Власне, в старі часи це [було символічним посиланням на test. Це працює так само, і у вас є однакові обмеження. Оскільки двійковий файл знає ім'я, з якого він був запущений, програма тестування може аналізувати параметри, поки не знайде параметр ]. Веселі трюки Unix.

Зверніть увагу , що в разі bash, [і testвбудовані функції (як уже згадувалося в коментарях), але в значній мірі ті ж самі обмеження.


1
Хоча testі [, звичайно, вбудовані команди в Bash, але ймовірно, що існує і зовнішній бінарний файл.
ilkkachu

1
Зовнішній бінарний файл для [не є символічним посиланням testна більшість сучасних систем.
Випадково832

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

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

Рекомендація : завжди використовуйте [].

Існують POSIX-еквіваленти для кожної [[ ]]конструкції, яку я бачив.

Якщо ви використовуєте [[ ]]:

  • втрачають портативність
  • змусити читача дізнатися тонкощі іншого розширення баш. [це лише звичайна команда зі дивним іменем, ніякої спеціальної семантики не задіяно.

¹ Натхненний еквівалентною [[...]]конструкцією в оболонці Корна

², але не вдається для деяких значень 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 vs Bash вже достатньо, щоб звести мене з розуму.
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Подивіться, 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.