Экранирование кавычек в Powershell - PullRequest
2 голосов
/ 27 марта 2020

В Powershell я хотел бы выполнить следующую команду:

bash -c "echo 'hello world!'"

(я знаю, что могу использовать echo или Write-Host напрямую, но мне нужно использовать bash -c), и я хотел бы, чтобы hello был заключен в одинарные кавычки, а world - в двойные. Другими словами, я хотел бы получить следующий вывод:

'hello' "world"!

Я должен экранировать эти кавычки?

Ответы [ 2 ]

1 голос
/ 28 марта 2020

Ответ HAL9256 полезен , но позвольте мне попытаться сформулировать проблему по-другому:

Чтобы получить требуемый дословный вывод, вот как должна выглядеть ваша команда если вы запустили его из самого bash :

# From Bash itself
$ bash -c "echo \"'hello' \\\"world\\\"!\""
'hello' "world"!

Bash исходного синтаксического анализа приведенного выше строкового литерала, внутри которого \ функционирует как escape-символ, означает, что Новый bash экземпляр видит следующий дословно текст в качестве аргумента -c:
echo "'hello' \"world\"!"
Поэтому, если вы выполняете это как команду напрямую в Bash это также даст желаемый результат.

Обратите внимание, как аргумент echo в целом заключен в (изначально \ -эскапед) "...", для надежности: без этого, например, если бы world было world & space, команда прервала бы ; аналогично world class превратится в world class (нормализация пробелов из-за расширений оболочки ).

Требуется множество символов \, так как задействовано 2 слоев побега :

  • Экранирование для вызов оболочки (в данном примере также bash), чтобы убедиться, что входная строка синтаксически допустима: \" встраивает один " и \\ один \.

  • Экранирование для target shell (bash), так что результирующая строка синтаксически допустима для it , учитывая, что результирующая строка анализируется как команда в этом случае.

Перевод команды для ее вызова из PowerShell требует адаптации первого слоя выше, что означает использование `, управляющего символа PowerShell вместо \.

. Следовательно, прямой перевод:

# SHOULD work, but DOESN'T as of PowerShell 7.0
PS> bash -c "echo `"'hello' \`"world\`"!`""
hello          # !! WRONG output.

Тем не менее, если вы напечатаете строковый литерал "echo `"'hello' \`"world\`"!`"" непосредственно в PowerShell, вы увидите, что он правильно возвращает v erbatim строка, которую bash нужно увидеть, как показано выше
(echo "'hello' \"world\"!")

Причина, по которой это не работает, заключается в том, что передача аргументов PowerShell внешние программы всегда ломались относительно встроенных двойных кавычек :

Этот ответ дает обзор , но если коротко, начиная с PowerShell 7.0:

В дополнение к удовлетворению собственных синтаксических требований PowerShell - этого достаточно, если вы вызываете PowerShell команды - , вы должны дополнительно \ -эскейп встроенные " символы в аргументах, передаваемых внешним программам .

Следовательно:

# OK, but the manual \-escaping shouldn't be necessary.
PS> bash -c "echo \`"'hello' \\\`"world\\\`"!\`""
'hello' "world"!

Почему текущее поведение нарушено:

Работа оболочки заключается в передаче аргументов, которые являются результатом собственного синтаксического анализа оболочки дословно целевой программе, делая все необходимое за сцены, чтобы убедиться, что - вы не должны иметь беспокоиться о любых экранирующих требованиях , кроме собственной PowerShell :

  • при Windows, из-за неотложной необходимости это означает построение команды за кулисами , применяет двойные кавычки и экранирует их при необходимости ; хотя двойные кавычки - это , применяемые PowerShell по требованию, это \ -экранирование " символов. встроено в аргументы , которое отсутствует.

  • На Unix -подобных платформах никаких дополнительных усилий не требуется: там программы получают аргументы непосредственно как массив из дословных значений.

Хотя конкретная команда, обсуждаемая в этом вопросе, делает перевод в PowerShell довольно простым, более коварные случаи, когда дополнительная необходимость в \ -escape - которая всегда громоздкая - также неожиданная , потому что эти команды работают как есть в bash сам :

PS> bash -c 'echo "hi, there"'
hi,   # !! " were stripped, so bash only saw `echo hi,` as the command string,
      # !! and `there` as a separate argument.
PS> /bin/echo '{ "foo": "bar" }' # Try to pass a JSON string
{ foo: bar } # !! BROKEN JSON
1 голос
/ 27 марта 2020

Поскольку вы вызываете программу (например, bash), «нормальные» правила для экранирующие символы кавычек не обязательно работают «одинаково». Вместо интерпретации строки в PowerShell (которая обычно следует «нормальным» правилам цитирования), вы передаете в программу аргументы . Это интерпретируется совершенно по-другому.

TLDR: для экранирования кавычек требуется следующая команда:

bash -c "echo \'Hello\' \\\""World\\\""!"

Выводы:

'Hello' "World"!

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

Во-первых, какова правильная команда в bash для вывода желаемой цитаты? Давайте попробуем обычные кавычки:

HAL9256@HAL9000:~$ echo 'Hello' "World"!
Hello World!

Нет кавычек. О, да! Я должен сбежать от них. В bash я должен убежать от них с обратными слешами (\):

HAL9256@HAL9000:~$ echo \'Hello\' \"World\"!
'Hello' "World"!

Там у нас это есть. Мы должны использовать одну обратную косую черту sh, чтобы правильно экранировать наши кавычки в bash. Итак, давайте подключим это к PowerShell:

PS C:\> bash -c "echo \'Hello\' \"World\"!"
/bin/bash: -c: line 0: unexpected EOF while looking for matching `"'
/bin/bash: -c: line 1: syntax error: unexpected end of file

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

PS C:\> bash -c "echo \'Hello\' \`"World\`"!"
'Hello' World!

Ну, это не ошибка , но все же не то, что мы хотим. Кавычки все еще не экранированы должным образом. Хмм. Здесь вы начинаете вводить 100 различных комбинаций символов, чтобы выяснить, какая правильная комбинация ;-).

Или, давайте go вернемся к bash и выясним, что может происходить. Во-первых, давайте вспомним, что строка, являющаяся echo ed, интерпретируется как строка. Итак, давайте поместим двойные кавычки вокруг нашего оператора echo, чтобы он обрабатывался как строка, и посмотрим, что он делает. Помните, что из этого мы хотим, чтобы выходные данные были такой же командой bash ранее, с обратной косой чертой:

HAL9256@HAL9000:~$ echo "\'Hello\' \"World\"!"
\'Hello\' "World"!

Что ж, в конечном итоге, то, что мы хотим отправить на bash, это та оригинальная строка с обратной косой чертой. Здесь мы видим, что обратные слэши избегают двойных кавычек и исчезают. Итак, добавляя еще косые черты:

HAL9256@HAL9000:~$ echo "\'Hello\' \\"World\\"!"
\'Hello\' \World\!

О, отлично. Мы избежали обратной косой черты. Как и в начале, продолжайте добавлять escape-символы, пока он не заработает ....

HAL9256@HAL9000:~$ echo "\'Hello\' \\\"World\\\"!"
\'Hello\' \"World\"!

Там! у нас есть выход. Теперь нам нужно ввести его в PowerShell:

PS C:\> bash -c "echo \'Hello\' \\\"World\\\"!"
/bin/bash: -c: line 0: unexpected EOF while looking for matching `"'
/bin/bash: -c: line 1: syntax error: unexpected end of file

О! да та же ошибка, что и раньше. Мы должны помнить, чтобы избежать двойных кавычек в PowerShell с обратной чертой:

PS C:\> bash -c "echo \'Hello\' \\\`"World\\\`"!"
'Hello' "World"!

Успех! Это заняло много времени, как bash -wize, так и PowerShell-wize. Другой способ, и, на мой взгляд, более легкий для понимания способ выхода из двойных кавычек - это использовать две двойные кавычки вместо обратной черты, например:

...