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

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

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

echo run after_bundle

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

 echo run after_bundle 

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

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

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

  • Почему printf лучше, чем echo?
  • Последствия для безопасности: забыть заключить переменную в оболочку bash / POSIX

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

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

или же

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

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

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

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

Просто дополнительная заметка поверх хорошего ответа @ Kusalananda .

 echo run after_bundle 

хорошо, потому что ни один из символов в этих 3 аргументах, передаваемых echo содержит символов, которые являются специальными для оболочки.

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

Все эти символы находятся в том, что POSIX называет переносимым набором символов . Эти символы должны присутствовать и кодироваться одинаково во всех наборах символов в системе POSIX².

Так что командная строка будет интерпретироваться одинаково независимо от локали.

Теперь, если мы начнем использовать символы вне этого переносимого набора символов, будет хорошей идеей заключить их в кавычки, даже если они не являются специальными для оболочки, потому что в другой локали байты, которые их составляют, могут интерпретироваться как разные символы, которые могут стать специально для оболочки. Обратите внимание, что вы используете echo или любую другую команду, проблема не в echo а в том, как shell анализирует свой код.

Например, в 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' качестве аргумента, они запускают его с $'voil\xc3' качестве аргумента, что означает, что он не будет правильно печатать voilà .

Это становится намного хуже с китайскими наборами символов, такими как BIG5, BIG5-HKSCS, GB18030, GBK, которые имеют много символов, кодировка которых содержит ту же кодировку, что и | , ` , \ (чтобы назвать худшее) (также этот нелепый SJIS, он же Microsoft Kanji, за исключением того, что вместо ¥ вместо ¥ , но большинство инструментов по-прежнему обрабатывают как \ , так как там он закодирован как 0x5c).

Например, если в китайском zh_CN.gb18030 стандарте zh_CN.gb18030 вы напишите такой скрипт:

 echo 詜 reboot 

Этот сценарий будет выводить 詜 reboot в локали с использованием GB18030 или GBK», « 唰 reboot в локали с использованием BIG5 или BIG5-HKSCS, но в локали C с использованием ASCII или локали с использованием ISO8859-15 или UTF-8» reboot будет выполнить, потому что кодировка равна 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 -nl \324| uname$ $ LC_ALL=C bash ./myscript | sed -nl Linux$ 

( uname был запущен).

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

Однако обратите внимание, что поскольку кодировка \ и ` встречается в кодировке некоторых из этих символов, лучше не использовать \ или "..." или $'...' (внутри ` и / или \ все еще находятся особый), но вместо '...' следует заключать символы в кавычки вне переносимого набора символов.

Я не знаю ни одной системы, в которой есть локаль, в которой кодировка содержит какой-либо символ (кроме ' самого себя»), чья кодировка содержит кодировку ' , поэтому эти '...' определенно должны быть самыми безопасными.

Обратите внимание, что несколько оболочек также поддерживают нотацию $'\uXXXX' для выражения символов на основе их кодовой точки Unicode. В таких shellх, как 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, кодировка которых не имеет символа \ ja_JP.SJIS ~ !

³ обратите внимание, что хотя многие системы (FreeBSD, Solaris, но не системы GNU) рассматривают U + 00A0 как [[:blank:]] в языковых стандартах UTF-8, немногие делают это в других языковых стандартах, таких как использующие ISO8859-15, возможно, чтобы избежать такого рода проблемы.