Чи небезпечно запускати відлуння без лапок?


11

Я бачив пару подібних тем, але вони мають на увазі не цитування змінних, що, як я знаю, може призвести до небажаних результатів.

Я бачив цей код і цікавився, чи можна ввести щось, що потрібно запустити, коли виконується цей рядок коду:

echo run after_bundle


Я наткнувся на це, коли мав: target = "*** LIVE SERVER ***"; ціль ехо: $ target; і *** розширився в папку з переліком ... 😬
Метт Паркінс

Відповіді:


17

Для конкретного випадку

echo run after_bundle

цитування не потрібне. Цитування не потрібне, оскільки аргумент - echoце статичні рядки, які не містять змінних розширень чи підстановок команд тощо. Вони є "лише двома словами" (і як вказує Stéphane , вони додатково будуються з набору переносних символів ).

"Небезпека" настає, коли ви маєте справу зі змінними даними, що оболонка може розширюватись або інтерпретувати. У таких випадках слід подбати про те, щоб оболонка робила правильно і щоб результат був тим, що призначено.

Наступні два запитання містять відповідну інформацію про це:


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

добре, тому що жоден із символів у цих 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, можливо, щоб уникнути подібних проблем.


У першому вашому абзаці ви кажете нам "... з символів у цих 3 аргументах, переданих до echo...", я рахую лише 2 аргументи, передані команді echo, аргументи, на які я можу порахувати, є, runі не after_bundleхочу пояснити, як ви порахував і дійшов до 3 аргументів?
Феррібіг

1
@ViktorFonic, див. Редагування щодо кількості аргументів (і що головна проблема не в тому echo). Дивіться (exec -a foo /bin/echo --help)у системі GNU та з оболонкою GNU, як передавати довільну аргумент довільного першого аргументу /bin/echo.
Стефан Шазелас

@Ferrybig Див. Редагування Стефана, виноска 1. Аргументи для командування у звичайному стилі C - це масив аргументів, аргумент [0] - саме ім'я, яке виконується. Начебто подібні $0та позиційні параметри в оболонках.
Сергій Колодяжний

Є 373 кодування, iconvв яке ESCперетворюється '. Спробуйте (як приклад):printf '\x1b'|iconv -f utf8 -t IBM-937|xxd
Ісаак

Існує 173 кодування, в яких деяка кодова точка (крім ESC) перетворюється на a '. Спробуйте 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
Ісаак
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.