Відповіді:
КОРОТКА ВІДПОВІДЬ
Як казали інші - ви завжди повинні цитувати змінні, щоб запобігти дивній поведінці. Тому використовуйте echo "$ foo" у, а не просто echo $ foo .
ДОВГИЙ ВІДПОВІДЬ
Я думаю, що цей приклад вимагає подальшого пояснення, оскільки відбувається більше, ніж може здатися на обличчі.
Я бачу, звідки приходить ваша плутанина, оскільки після того, як ви запустили перший приклад, ви, мабуть, подумали собі, що оболонка, очевидно, робить:
Отже, з вашого першого прикладу:
me$ FOO="BAR * BAR"
me$ echo $FOO
Після розширення параметра еквівалентно:
me$ echo BAR * BAR
А після розширення імені файлу еквівалентно:
me$ echo BAR file1 file2 file3 file4 BAR
А якщо ви просто наберете echo BAR * BAR
в командному рядку, ви побачите, що вони еквівалентні.
Тож ви, напевно, думали собі, "якщо я уникну *, я можу запобігти розширенню імені файлу"
Отже, з вашого другого прикладу:
me$ FOO="BAR \* BAR"
me$ echo $FOO
Після розширення параметра слід дорівнювати:
me$ echo BAR \* BAR
А після розширення імені файлу має бути рівнозначним:
me$ echo BAR \* BAR
І якщо ви спробуєте ввести "echo BAR \ * BAR" безпосередньо в командному рядку, він дійсно надрукує "BAR * BAR", оскільки розширення імені файлів запобігає втечі.
Так чому ж використання $ foo не спрацювало?
Це тому, що відбувається третє розширення - видалення цитат. З видалення цитати вручну цитатою є:
Після попередніх розширень всі нецитовані події символів '\', '' 'і' '', які не були результатом одного з вищезазначених розширень, видаляються.
Отже, що відбувається, коли ви вводите команду безпосередньо в командний рядок, символ втечі не є результатом попереднього розширення, тому BASH видаляє її перед відправленням команді echo, але у другому прикладі "\ *" було результат попереднього розширення параметра, тому він НЕ видаляється. В результаті ехо отримує "\ *" і саме це друкує.
Зауважте, різниця між першим прикладом - "*" не входить до символів, які будуть видалені шляхом видалення цитата.
Сподіваюся, це має сенс. Врешті-решт висновок той же - просто використовуйте цитати. Я просто думав, що поясню, чому втеча, яка логічно повинна працювати, якщо грають лише розширення параметра та імені файлів, не працювала.
Повне пояснення розширень BASH дивіться на:
http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions
Я додам трохи до цієї старої нитки.
Зазвичай ви користуєтесь
$ echo "$FOO"
Однак у мене були проблеми навіть із цим синтаксисом. Розглянемо наступний сценарій.
#!/bin/bash
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"
Ці *
потреби повинні бути передані дослівно curl
, але виникають ті ж проблеми. Наведений вище приклад не буде працювати (він розшириться до назви файлів у поточному каталозі), а також не буде \*
. Ви також не можете цитувати, $curl_opts
оскільки він буде визнаний єдиним (недійсним) варіантом для curl
.
curl: option -s --noproxy * -O: is unknown
curl: try 'curl --help' or 'curl --manual' for more information
Тому я рекомендую використовувати bash
змінну $GLOBIGNORE
для запобігання розширення імені файлів взагалі, якщо застосовується до глобального шаблону, або використовувати set -f
вбудований прапор.
#!/bin/bash
GLOBIGNORE="*"
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1" ## no filename expansion
Звернення до оригінального прикладу:
me$ FOO="BAR * BAR"
me$ echo $FOO
BAR file1 file2 file3 file4 BAR
me$ set -f
me$ echo $FOO
BAR * BAR
me$ set +f
me$ GLOBIGNORE=*
me$ echo $FOO
BAR * BAR
SELECT * FROM etc.
це єдиний спосіб, який працює.
echo "$FOO"
Можливо, варто скористатися звичкою використовувати, printf
а не echo
командний рядок.
У цьому прикладі це не дає великої користі, але може бути кориснішим при більш складних результатах.
FOO="BAR * BAR"
printf %s "$FOO"