Опасно ли запускать эхо без кавычек?

11

Я видел пару похожих тем, но они не ссылаются на переменные, которые, как я знаю, могут привести к нежелательным результатам.

Я видел этот код, и мне было интересно узнать, можно ли внедрить что-то для запуска при выполнении этой строки кода:

echo run after_bundle

Виктор Фоник
источник
Я столкнулся с этим, когда у меня было: target = "*** LIVE SERVER ***"; echo target: $ target; и *** расширился до списка папок ... 😬
Мэтт Паркинс

Ответы:

17

Для конкретного случая

echo run after_bundle

цитирование не нужно. Заключение в кавычки не требуется, поскольку аргументом echoявляются статические строки, которые не содержат раскрытия переменных или подстановок команд и т. Д. Они являются «просто двумя словами» (и, как указывает Стефан , они дополнительно создаются из переносимого набора символов ).

«Опасность» возникает, когда вы имеете дело с переменными данными, которые оболочка может расширять или интерпретировать. В таких случаях необходимо позаботиться о том, чтобы оболочка сделала правильные вещи и чтобы результат был именно тем, что предполагалось.

Следующие два вопроса содержат соответствующую информацию об этом:


echoиногда используется для «защиты» потенциально опасных команд в ответах на этом сайте. Например, я могу показать, как удалить файлы или переместить файлы в новое место назначения, используя

echo rm "${name##*/}.txt"

или

echo mv "$name" "/new_dir/$newname"

Это выведет команды на терминал вместо того, чтобы фактически удалять или переименовывать файлы. Затем пользователь может проверить команды, решить, что они выглядят нормально, удалить echoи снова запустить.

Ваша команда echo run after_bundleможет быть инструкцией для пользователя или «закомментированным» фрагментом кода, который слишком опасен для выполнения, не зная последствий.

Используя , echoкак это, один должен знать , что модифицированная команда делает , и один должен гарантировать , что измененная команда на самом деле является безопасным (она потенциально не будет , если в нем содержится переназначения, и использовать его на трубопроводе не работает, и т.д.)

Кусалананда
источник
Однако добавления кавычек недостаточно для того, чтобы знать, что будет делать оболочка - точно так же, как вы не можете сказать, что echo rm "first file.txt" "second file.txt"это каким-либо образом отличается от того echo rm "first" "file.txt" "second" "file.txt", что результат обоих одинаков. Если вы хотите сгенерировать команду оболочки в качестве вывода, нужно использовать printf '%q ' rm "first file.txt" "second file.txt"; echoили что-то эквивалентное, которое повторно генерирует синтаксические кавычки, которые оценивают argvпереданные.
Чарльз Даффи
@CharlesDuffy Я действительно надеюсь, что никто не копирует и вставляет вывод отладочной информации и запускает его в оболочке!
Кусалананда
1
Генерация команд оболочки и последующая их передача shне являются чем-то необычным, и люди видят вопрос: «Почему fooработает, когда я запускаю его в командной строке, а этот сценарий, который выдает эту точную строку echoперед строкой, нет? " происходит все время здесь. Более того, вывод отладочной информации бесполезен, если он скрывает ваши ошибки - и если ваши ошибки связаны с цитированием, то echoне будут их обнаруживать.
Чарльз Даффи,
27

Просто дополнительная заметка поверх прекрасного ответа @ 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, возможно, чтобы избежать такого рода проблем.

Стефан Шазелас
источник
В вашем первом абзаце вы говорите нам «... символов в этих 3 аргументах, переданных в echo...», я считаю только 2 аргумента, передаваемых команде echo, аргументы, которые я могу сосчитать, runи after_bundle, как объяснить, как вы посчитал и получил 3 аргумента?
Ferrybig
1
@ViktorFonic, см. Правку о количестве аргументов (и это главная проблема не с echo). См. В (exec -a foo /bin/echo --help)системе GNU и с оболочкой GNU, как передать произвольный первый аргумент /bin/echoутилите.
Стефан Шазелас
@Ferrybig См. Правку Стефана, сноска 1. Аргументы команды в обычном стиле C являются массивом аргументов, а argv [0] является самим именем исполняемого файла. Вроде схожи $0и позиционные параметры в оболочках.
Сергей Колодяжный
Есть 373 кодировки, iconvв которые ESCпреобразуется в '. Попробуйте (в качестве примера):printf '\x1b'|iconv -f utf8 -t IBM-937|xxd
NotAnUnixNazi
Существует 173 кодирования, в которых некоторая кодовая точка (кроме ESC) преобразуется в '. Попробуй printf '\u2804' | iconv -f utf8 -t BRF | xxd. Есть кодировки, в которых есть много кодовых точек, которые становятся '. Около 8695 кодовых точек в UCS-4 становятся '. Попробуй printf '\U627' | iconv -cf utf-8 -t UCS-4. Несколько (37) кодировок преобразуют символ 0x127 в a '. Попробуйтеprintf '\U127' | iconv -cf utf8 -t UCS2 |xxd
NotAnUnixNazi