Лише додаткова записка зверху до тонкої відповіді @ Kusalananda .
echo run after_bundle
добре, тому що жоден із символів у цих 3 аргументах¹ не echo
містив символів, які є спеціальними для оболонки.
І (додатковий пункт, який я хочу тут зробити) немає жодної локальної системи, куди ці байти могли б перекласти символи, які є спеціальними для оболонки.
Усі ці символи містяться в тому, що POSIX називає портативний набір символів . Ці символи повинні бути присутніми та кодуватися однаково у всіх наборах символів у системі POSIX².
Отже, цей командний рядок буде інтерпретуватися однаково, незалежно від мови.
Тепер, якщо ми почнемо використовувати символи поза цим портативним набором символів, непогано буде цитувати їх, навіть якщо вони не є особливими для оболонки, оскільки в іншій мові байти, що їх складають, можуть інтерпретуватися як різні символи, які можуть стати спеціальні для оболонки. Зауважте, що це ви використовуєте echo
чи будь-яку іншу команду, проблема полягає не в тому, echo
а в тому, як оболонка аналізує свій код.
Наприклад, в UTF-8:
echo voilà | iconv -f UTF-8 -t //TRANSLIT
Це à
кодується як 0xc3 0xa0. Тепер, якщо у вас є такий рядок коду в скрипті оболонки, а скрипт оболонки викликається користувачем, який використовує локаль, чия набір даних не є UTF-8, ці два байти можуть створювати дуже різні символи.
Наприклад, у fr_FR.ISO8859-15
локалі, типовому французькому мові, використовуючи стандартну однобайтову схему, яка охоплює французьку мову (те саме, що використовується для більшості західноєвропейських мов, включаючи англійську), цей байт 0xc3 інтерпретується як Ã
символ, а 0xa0 - як не- ламання космічного персонажа.
І в кількох системах, таких як NetBSD³, цей нерозривний простір вважається порожнім символом ( isblank()
він повертає істину, він відповідає [[:blank:]]
), і оболонки, як bash
отже, трактують його як роздільник маркер у своєму синтаксисі.
Це означає, що замість запуску echo
з $'voil\xc3\xa0'
аргументом вони запускають його $'voil\xc3'
як аргумент, а це означає, що він не буде друкуватись voilà
правильно.
Це стає набагато гірше, якщо китайські набори символів, такі як BIG5, BIG5-HKSCS, GB18030, GBK, містять багато символів, кодування яких містить те саме кодування, що |
і `
, \
(щоб назвати найгірше) (також той смішний SJIS, він же Microsoft Kanji, за винятком що це ¥
замість \
, але все ще трактується як \
більшість інструментів, оскільки там закодовано як 0x5c).
Наприклад, якщо у zh_CN.gb18030
китайській мові, ви пишете такий сценарій, як:
echo 詜 reboot
Цей скрипт буде виводитися 詜 reboot
в локальну мову за допомогою GB18030 або GBK, 唰 reboot
в локалі за допомогою BIG5 або BIG5-HKSCS, але в мові C, використовуючи ASCII або локалі з використанням ISO8859-15 або UTF-8, призведе reboot
до запуску, оскільки кодування GB18030 з 詜
0xd4 0x7c і 0x7c - це кодування |
в ASCII, тому ми закінчуємо виконання:
echo �| reboot
(що , що представляє, проте, байт 0xd4 відображається в локалі). Приклад використання менш шкідливих uname
замість reboot
:
$ echo $'echo \u8a5c uname' | iconv -t gb18030 > myscript
$ LC_ALL=zh_CN.gb18030 bash ./myscript | sed -n l
\324| uname$
$ LC_ALL=C bash ./myscript | sed -n l
Linux$
( uname
був запущений).
Тому я радив би цитувати всі рядки, що містять символи поза набором переносних символів.
Однак зауважимо , що оскільки кодування \
і `
знаходяться в кодуванні деяких з цих символів, то краще не використовувати \
або "..."
чи $'...'
(всередині яких `
і / або \
по - , як і раніше особливий), але '...'
замість того, щоб цитувати символи за межами портативного набору символів.
Мені невідома жодна система, яка має локаль, де шасі має будь-який символ (крім '
самого себе, звичайно), кодування якого містить кодування '
, тому вони, '...'
безумовно, повинні бути найбезпечнішими.
Зауважте, що кілька оболонок також підтримують $'\uXXXX'
позначення для вираження символів на основі їх кодової точки Unicode. У оболонках, як zsh
і bash
, символ вставляється закодованим у шаблоні локалу (хоча це може спричинити несподіване поведінку, якщо ця діаграма не має цього символу). Це дозволяє вам не вставляти символи, що не належать до ASCII, у ваш код оболонки.
Отже вище:
echo 'voilà' | iconv -f UTF-8 -t //TRANSLIT
echo '詜 reboot'
Або:
echo $'voil\u00e0'
echo $'\u8a5c reboot'
(із застереженням він може порушити сценарій під час запуску в локалях, у яких немає цих символів).
Або ще краще, оскільки \
це також особливе echo
(або принаймні деякі echo
реалізації, принаймні сумісні з Unix):
printf '%s\n' 'voilà' | iconv -f UTF-8 -t //TRANSLIT
printf '%s\n' '詜 reboot'
(зауважте, що \
також є особливим у першому аргументі printf
, тому символів, що не належать до ASCII, також краще уникати там, якщо вони можуть містити кодування \
).
Зауважте, що ви також можете зробити:
'echo' 'voilà' | 'iconv' '-f' 'UTF-8' '-t' '//TRANSLIT'
(це було б надмірно, але це могло б дати вам спокій, якщо ви не впевнені, які символи є в портативному наборі символів)
Також переконайтеся, що ніколи не використовуйте давню `...`
форму заміни команд (яка вводить інший рівень зворотної косої риси), а використовуйте $(...)
замість цього.
¹ технічно, echo
також передаються в якості аргументу в echo
корисність (щоб сказати йому , як він був запущений), це argv[0]
і argc
є 3, хоча в більшості оболонок в даний час echo
є вбудованим, так що exec()
з /bin/echo
файлу зі списком 3 аргументів моделюються оболонки. Також загальним є розгляд списку аргументів, що починається з другого ( argv[1]
до argv[argc - 1]
), оскільки саме такі команди в основному діють.
² помітним винятком є те, що безглуздий ja_JP.SJIS
локал систем FreeBSD, у якого набір не має \
ні норм ~
!
Зверніть увагу, що хоча багато систем (FreeBSD, Solaris, а не GNU) розглядають U + 00A0 як локальний інтерфейс [[:blank:]]
UTF-8, мало хто працює в інших локалях, як у тих, що використовують ISO8859-15, можливо, щоб уникнути подібних проблем.