Чому розширення параметрів з пробілами без лапок працює всередині подвійних дужок "[[", але не всередині окремих дужок "["?


85

Мене плутають у використанні одинарних чи подвійних дужок. Подивіться на цей код:

dir="/home/mazimi/VirtualBox VMs"

if [[ -d ${dir} ]]; then
    echo "yep"
fi

Він прекрасно працює, хоча рядок містить пробіл. Але коли я зміню його на одну дужку:

dir="/home/mazimi/VirtualBox VMs"

if [ -d ${dir} ]; then
    echo "yep"
fi

Він говорить:

./script.sh: line 5: [: /home/mazimi/VirtualBox: binary operator expected

Коли я зміню його на:

dir="/home/mazimi/VirtualBox VMs"

if [ -d "${dir}" ]; then
    echo "yep"
fi

Це чудово працює. Чи може хтось пояснити, що відбувається? Коли я повинен призначити подвійні лапки навколо змінних, як, "${var}"щоб запобігти проблемам, викликаним пробілами?



Більш загальне питання щодо питання: unix.stackexchange.com/questions/306111/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Відповіді:


83

Одинарна дужка [- це фактично псевдонім для testкоманди, це не синтаксис.

Одним із недоліків (з багатьох) одиничної дужки є те, що якщо один або кілька операндів, які він намагається оцінити повернути порожній рядок, він поскаржиться, що очікував два операнди (двійкові). Ось чому ви бачите, що це роблять люди [ x$foo = x$blah ], це xгарантії того, що операнд ніколи не оцінить порожній рядок.

Подвійна дужка [[ ]], з іншого боку, є синтаксисом і набагато більш здатною, ніж [ ]. Як ви з’ясували, у нього немає єдиного випуску операндів, і він також забезпечує більше C-подібного синтаксису з >, <, >=, <=, !=, ==, &&, ||операторами.

Моя рекомендація така: Якщо ваш перекладач є #!/bin/bash, то завжди користуйтеся[[ ]]

Важливо зауважити, що [[ ]]підтримується не всіма оболонками POSIX, проте багато оболонок підтримують його, такі як zshі kshна додаток доbash


16
Проблема порожнього рядка (та багато інших) вирішується за допомогою лапок. "Х" - це охоплення іншого виду проблем: де операнди можуть прийматися як оператори . Наприклад , коли $fooє !або (чи -n... Ця проблема не повинна бути проблемою з POSIX оболонками , де число аргументів (крім [і ]) не більш , ніж чотири.
Стефан Шазелас

21
Щоб уточнити, [ x$foo = x$blah ]це так само неправильно, як [ $foo = $bar ]. [ "$foo" = "$bar" ]є правильним у будь-якій оболонці, сумісною з POSIX, [ "x$foo" = "x$bar" ]працює в будь-якій оболонці Борна, але xце не для випадків, коли у вас порожні рядки, але там, де вони $fooможуть бути !або -n...
Stéphane Chazelas

1
Цікава семантика у цій відповіді. Я не впевнений, що ви маєте на увазі під синтаксисом * not * . Звичайно, [це не псевдонім для test. Якби це було, то testприйняв би квадратну дужку, що закривається. test -n foo ]. Але testні, і [вимагає цього. [є ідентичним testу всіх інших аспектах, але це не так, як aliasпрацює. Bash описує [і testяк вбудовану оболонку , але [[як ключове слово оболонки .
kojiro

1
Ну, у bash [вбудована оболонка, але / usr / bin / [також виконується. Традиційно це посилання на / usr / bin / test, але в сучасних gnu coreutils є окремим бінарним. Традиційна версія тесту розглядає argv [0], щоб побачити, чи він викликається як [, а потім шукає відповідність].
Еван

Мій баш не працює >=і<=
Студент

51

[Команда звичайна команда. Хоча більшість оболонок забезпечує це як вбудований для ефективності, він підкоряється нормальним синтаксичним правилам оболонки. [точно рівнозначний test, за винятком випадків, коли [потрібен ]як останній аргумент, і testне.

Подвійні дужки - [[ … ]]це особливий синтаксис. Вони були введені в ksh (через кілька років [), тому що [правильно використовувати правильно і [[дозволяють отримати нові приємні доповнення, які використовують спеціальні символи оболонки. Наприклад, можна писати

[[ $x = foo && $y = bar ]]

тому що весь умовний вираз аналізується оболонкою, тоді як [ $x = foo && $y = bar ]спочатку він буде розділений на дві команди [ $x = fooі $y = bar ]розділений &&оператором. Аналогічно подвійні дужки дозволяють такі речі, як синтаксис відповідності шаблону, наприклад, [[ $x == a* ]]перевірити, чи починається значення xз a; в окремих дужках це розшириться a*до списку файлів, імена яких починаються з aпоточного каталогу. Подвійні дужки вперше були введені в ksh і доступні лише у ksh, bash та zsh.

Усередині поодинокі дужки, ви повинні використовувати подвійні лапки підстановок змінних, як і в більшості інших місць, тому що вони тільки аргументи команди (що трапляється, [команда). Усередині подвійних дужок вам не потрібні подвійні лапки, тому що оболонка не робить розбиття слів або глобулювання: це розбір умовного виразу, а не команди.

Виняток є, [[ $var1 = "$var2" ]]коли вам потрібні цитати, якщо ви хочете порівняти рядки байт-байт, інакше $var2це буде зразок, який повинен відповідати $var1.

Одне з чого ви не можете зробити [[ … ]]- це використовувати змінну в якості оператора. Наприклад, це цілком законно (але рідко корисно):

if [ -n "$reverse_sort" ]; then op=-gt; else op=-lt; fi

if [ "$x" "$op" "$y" ]; then 

У вашому прикладі

dir="/home/mazimi/VirtualBox VMs"
if [ -d ${dir} ]; then 

команда всередині ifє [з 4 -х аргументів -d, /home/mazimi/VirtualBox, VMsі ]. Оболонка розбирає, -d /home/mazimi/VirtualBoxа потім не знає, що з цим робити VMs. Вам потрібно не допустити поділу слів, ${dir}щоб отримати добре сформовану команду.

Взагалі кажучи, завжди використовуйте подвійні лапки навколо підстановок змінних та команд, якщо ви не знаєте, що хочете виконати розбиття слів і глобалізацію результату. Основні місця, де безпечно не використовувати подвійні лапки, - це:

  • у призначенні: foo=$bar(але зауважте, що вам потрібні подвійні лапки в export "foo=$bar"присвоєннях або в масиві на зразок array=("$a" "$b"));
  • у caseзаяві case $foo in …:;
  • в подвійних дужках , за винятком на правій стороні =або ==оператора (якщо ви не хочете робити зіставлення з зразком): [[ $x = "$y" ]].

У всьому цьому правильним є використання подвійних лапок, тож ви можете також пропустити розширені правила та користуватися цитатами весь час.


1
@StephaneChazelas Моя помилка. Виявляється, що [це справді із системи III в 1981 році , я думав, що вона старша.
Жиль

8

Коли я повинен призначити подвійні лапки навколо змінних, як, "${var}"щоб запобігти проблемам, викликаним пробілами?

Імпліцит у цьому питанні є

Чому недостатньо добре?${variable_name}

${variable_name} не означає, що ви думаєте, що це робить ...

... якщо ви думаєте , що має нічого спільного з проблемами , викликаними пробілами (в значеннях змінних). добре для цього:${variable_name}

$ bar=foo
$ bard=Shakespeare
$ echo $bard
Shakespeare
$ echo ${bar}d
food

і більше нічого! 1  робить який - або користіякщо ви не відразу після його з персонажемякий може бути частиною імені змінної: літери (-або-), підкреслення () або цифри (-). І навіть тоді ви можете обійти це:${variable_name}AZaz_09

$ echo "$bar"d
food

Я не намагаюся перешкодити його використанню - echo "${bar}d"це, мабуть, найкраще рішення тут, - але перешкодити людям покладатися на дужки замість лапок, або застосовувати дужки інстинктивно, а потім запитати: «Чи мені також потрібні цитати ? "  Ви завжди повинні використовувати лапки, якщо у вас немає вагомих причин цього не робити, і ви впевнені, що знаєте, що робите.
_________________
1    За винятком, звичайно, того, що більш фантазійні форми розширення параметрів , наприклад, і , будуються на синтаксисі. Крім того, вам потрібно використовувати , і т.д., для посилання на 10, 11 і т.д., позиційні параметри - котирування не допоможуть вам у цьому.${parameter:-[word]}${parameter%[word]}${parameter}${10}${11}


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.