Просто дополнительная заметка поверх прекрасного ответа @ Kusalananda .
echo run after_bundle
это хорошо, потому что ни один из символов в этих трех аргументах не передается, чтобы 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()
в нем он возвращает true, ему соответствует [[:blank:]]
), и оболочки вроде бы bash
поэтому рассматривают его как разделитель токенов в своем синтаксисе.
Это означает, что вместо запуска echo
с $'voil\xc3\xa0'
аргументом as они запускают его с $'voil\xc3'
аргументом as, что означает, что он не будет печататься voilà
правильно.
Это становится намного хуже китайскими наборами символов , такими как BIG5, BIG5-HKSCS, GB18030, ГКИ , которые имеют много символов , чьих кодирования содержит ту же кодировку, что |
, `
, \
(назвать наихудшее) (также , что нелепое SJIS, иначе Microsoft кандзи, за исключением что это ¥
вместо \
, но все еще рассматривается как \
большинство инструментов, поскольку это закодировано как 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]
to argv[argc - 1]
), поскольку это те команды, на которые в основном воздействуют команды.
² заметное исключение из того, что это нелепая ja_JP.SJIS
локаль систем FreeBSD, чья кодировка не имеет \
ни ~
символа!
³ обратите внимание, что хотя многие системы (FreeBSD, Solaris, но не GNU) рассматривают U + 00A0 как a [[:blank:]]
в локалях UTF-8, немногие делают это в других локалях, таких как ISO8859-15, возможно, чтобы избежать такого рода проблем.