Собственный ответ eplawless просто и эффективно решает его конкретную проблему: он заменяет все "
экземпляры во всем списке аргументов на \"
, как Bash требует представления двойных кавычек внутри строки с двойными кавычками.
Чтобы в целом ответить на вопрос о том, как избежать двойных кавычек внутри строки с двойными кавычками, используйтеcmd.exe
интерпретатор командной строки Windows (будь то в командной строке - часто ошибочно называемой «подсказкой DOS» - или в командном файле): Смотрите внизу, чтобы посмотреть на PowerShell .
tl; dr :
Вы должны использовать""
при передаче строки в (другой) командный файл, и вы можете использовать ""
с приложениями, созданными с помощью компиляторов Microsoft C / C ++ /. NET (которые также принимают \"
), которые в Windows включают Python и Node.js :
\"
это требуется - как единственный вариант - многими другими программами , (! например, Ruby, Perl, и даже Microsoft собственный Windows PowerShell ()), но ЕГО ИСПОЛЬЗОВАНИЕ НЕ SAFE :
\"
это то, что требуется многим исполняемым файлам и интерпретаторам, включая Windows PowerShell, при передаче строк извне - или, в случае компиляторов Microsoft, поддержка в качестве альтернативы ""
- в конечном итоге, однако, целевая программа должна анализировать список аргументов .
- Пример:
foo.exe "We had 3\" of rain."
- ОДНАКО ИСПОЛЬЗОВАНИЕ
\"
МОЖЕТ ПРИВЕСТИ К НЕЖЕЛАТЕЛЬНОМУ, ПРОИЗВОЛЬНОМУ ВЫПОЛНЕНИЮ КОМАНД и / или ПЕРЕПРАВКАМ ВХОДА / ВЫХОДА :
- Этот риск представляют следующие символы:
& | < >
- Например, следующее приводит к непреднамеренному выполнению
ver
команды; см. ниже объяснение и следующий пункт для обходного пути:
foo.exe "3\" of snow" "& ver."
- Для Windows PowerShell ,
\""
и"^""
надежные, но ограниченные варианты (смотри раздел «Вызов CLI PowerShell в ...» ниже).
Если вам необходимо использовать \"
, есть только 3 безопасных подхода , которые, однако, довольно громоздки : Совет TS за его помощь.
Используя (возможно, выборочное ) отложенное расширение переменной в вашем пакетном файле, вы можете сохранить литерал \"
в переменной и ссылаться на эту переменную внутри "..."
строки с помощью !var!
синтаксиса - см . Полезный ответ TS .
- Вышеупомянутый подход, несмотря на его громоздкость, имеет то преимущество, что вы можете применять его методично и надежно работать с любыми входными данными.
Только с БУКВАЛЬНЫМИ строками - те, которые НЕ включают ПЕРЕМЕННЫЕ - вы получаете аналогичный методический подход: категорически ^
-экранировать все cmd.exe
метасимволы: " & | < >
и - если вы также хотите подавить расширение переменных - %
:
foo.exe ^"3\^" of snow^" ^"^& ver.^"
В противном случае вы должны сформулировать свою строку на основе распознавания, какие части строки cmd.exe
считаются некотируемыми из-за неправильной интерпретации\"
как закрывающие разделители:
в буквальных частях, содержащих метасимволы оболочки: - ^
экранировать их; используя приведенный выше пример, это &
необходимо ^
-экранировать:
foo.exe "3\" of snow" "^& ver."
частями со %...%
ссылками на переменные -style : убедитесь, что они cmd.exe
считаются частью "..."
строки и что значения переменных сами по себе не имеют встроенных несбалансированных кавычек - что даже не всегда возможно .
Для получения дополнительной информации читайте дальше.
Задний план
Примечание: это основано на моих собственных экспериментах. Дай мне знать, если я ошибаюсь.
POSIX-подобные оболочки, такие как Bash в Unix-подобных системах, токенизируют список аргументов (строку) перед передачей аргументов индивидуальной целевой программе: среди других расширений они разделяют список аргументов на отдельные слова (разделение слов) и удаляют символы кавычек из результирующие слова (удаление кавычек). Целевая программа вручено массив из отдельных аргументов , с синтаксическими цитаты удалены .
Напротив, интерпретатор команд Windows, по-видимому, не токенизирует список аргументов, а просто передает единственную строку, содержащую все аргументы, включая символы в кавычках. - в целевую программу.
Однако перед передачей в целевую программу одной строки выполняется некоторая предварительная обработка: ^
escape-символы. строки, не заключенные в двойные кавычки, удаляются (они экранируют следующий символ), а ссылки на переменные (например, %USERNAME%
) интерполируются первыми.
Таким образом, в отличие от Unix, целевая программа несет ответственность за синтаксический анализ, чтобы проанализировать строку аргументов и разбить ее на отдельные аргументы с удаленными кавычками. Таким образом, для разных программ гипотетически могут потребоваться разные методы экранирования, и нет единого механизма экранирования, который гарантированно работал бы со всеми программами - https://stackoverflow.com/a/4094897/45375 содержит отличный фон по анархии, которая является командной строкой Windows парсинг.
На практике \"
очень распространено, но НЕ БЕЗОПАСНО , как упоминалось выше:
Так как cmd.exe
сам по себе не признает \"
как бежала двойную кавычку, он может неправильно истолковать позже лексемы в командной строке, без кавычек и потенциально интерпретировать их как команду и / или ввод / вывод перенаправления .
В двух словах: проблема поверхности, если любой из следующих символов следовать отверстию или несбалансированное \"
:& | < >
; например:
foo.exe "3\" of snow" "& ver."
cmd.exe
видит следующие токены, полученные в результате неправильной интерпретации \"
как обычных двойных кавычек:
"3\"
of
snow" "
- отдых:
& ver.
Так как cmd.exe
считает , что & ver.
это не котировочные , он интерпретирует его как &
(оператор командной секвенировании), за которым следует имя команды для выполнения ( ver.
- .
игнорируются; ver
отчеты cmd.exe
версии информации «ы).
Общий эффект:
- Сначала
foo.exe
вызывается с первыми 3 токенами.
- Затем команда
ver
выполняется.
Даже в тех случаях, когда случайная команда не причинит вреда, ваша общая команда не будет работать должным образом, поскольку ей передаются не все аргументы.
Многие компиляторы / интерпретаторы распознают ТОЛЬКО\"
- например, компилятор GNU C / C ++, Python, Perl, Ruby, даже собственную оболочку Windows PowerShell от Microsoft при вызове из cmd.exe
- и, кроме (с ограничениями) для Windows PowerShell \""
, для них нет простого решения к этой проблеме.
По сути, вам нужно заранее знать, какие части вашей командной строки неверно интерпретируются как не ^
заключенные в кавычки , и выборочно экранировать все экземпляры& | < >
в этих частях.
Напротив, использование ""
БЕЗОПАСНО , но, к сожалению, поддерживается только исполняемыми файлами на основе компилятора Microsoft и пакетными файлами (в случае пакетных файлов с особенностями, описанными выше), что примечательно исключает PowerShell - см. Следующий раздел.
Вызов интерфейса командной строки PowerShell из cmd.exe
или POSIX-подобных оболочек:
Примечание. В нижнем разделе показано, как цитирование обрабатывается внутри PowerShell.
При вызове извне - например, из cmd.exe
командной строки или командного файла:
PowerShell [Core] v6 + теперь правильно распознает""
(в дополнение к\"
), что безопасно в использовании и сохраняет пробелы .
pwsh -c " ""a & c"".length "
не ломается и правильно уступает 6
Windows PowerShell (устаревшая версия, последняя версия которой - 5.1) распознает только \"
and, в Windows также """
и более надежный \""
/"^""
(хотя внутри PowerShell использует`
как escape-символ в строках с двойными кавычками, а также принимает""
- см. Нижний раздел):
Вызов Windows PowerShell изcmd.exe
командного файла:
""
ломается , потому что принципиально не поддерживается:
powershell -c " ""ab c"".length "
-> ошибка «В строке отсутствует терминатор»
\"
и """
работают в принципе , но небезопасно :
powershell -c " \"ab c\".length "
работает по назначению: он выводит 5
(обратите внимание на 2 пробела)
- Но это небезопасно, потому что
cmd.exe
метасимволы нарушают команду, если не экранированы:
powershell -c " \"a& c\".length "
breaks , из-за &
, которые должны быть экранированы как^&
\""
является безопасным , но нормализуют интерьер пробелы , которые могут быть нежелательными:
powershell -c " \""a& c\"".length "
выводит 4
(!), потому что 2 пробела нормализованы до 1.
"^""
это лучший выбор для Windows PowerShell конкретно , где она является безопасной и пробельным сохраняющим, но с PowerShell Ядром (на Windows) , это то же самое , как\""
, например, whitespace- нормализации . Заслуга Venryx для открытия этого подхода.
powershell -c " "^""a& c"^"".length "
работает : не ломается - несмотря на &
- и выводит 5
, т.е. правильно сохраненные пробелы.
PowerShell Core : pwsh -c " "^""a& c"^"".length "
работает , но выводит 4
, т.е. нормализует пробелы , как и \""
делает.
На Unix-подобных платформах (Linux, macOS) при вызове интерфейса командной строки PowerShell [Core]pwsh
из POSIX-подобной оболочки, такой какbash
:
Вы должны использовать\"
, что, однако, безопасно и сохраняет пробелы :
$ pwsh -c " \"a& c|\".length"
Связанная информация
^
может использоваться только как escape-символ в строках без кавычек - внутри строк с двойными кавычками, ^
не является специальным и обрабатывается как литерал.
- ПРЕДОСТЕРЕЖЕНИЕ : использование
^
параметров in, переданных в call
инструкцию, нарушено (это относится как к использованию call
: вызова другого пакетного файла или двоичного файла, так и вызова подпрограммы в том же пакетном файле):
^
экземпляры в значениях в двойных кавычках необъяснимо удваиваются , изменяя передаваемое значение: например, если переменная %v%
содержит буквальное значение a^b
, call :foo "%v%"
присваивает "a^^b"
(!) (!) %1
(первому параметру) в подпрограмме :foo
.
- Некотируемые использование
^
с call
будет полностью неработоспособным в том , что ^
не может больше использоваться , чтобы экранировать специальные символы : например,call foo.cmd a^&b
спокойно перерывы (вместо прохождения буквальнымa&b
тожеfoo.cmd
, как было бы в случае безcall
) -foo.cmd
никогда дажевызывается, по крайней мерена ОС Windows (!) 7.
К%
сожалению, экранирование литерала - это особый случай , который требует особого синтаксиса в зависимости от того, указана ли строка в командной строке или внутри командного файла ; см. https://stackoverflow.com/a/31420292/45375
- Вкратце: внутри командного файла используйте
%%
. В командной строке %
нельзя экранировать, но если вы поместите a ^
в начале, конце или внутри имени переменной в строке без кавычек (например, echo %^foo%
), вы можете предотвратить расширение переменной (интерполяцию); %
экземпляры в командной строке, которые не являются частью ссылки на переменную, обрабатываются как литералы (например, 100%
).
Как правило, для безопасной работы со значениями переменных, которые могут содержать пробелы и специальные символы :
- Назначение : Вложите как имя переменной и значение в одной паре двойных кавычках ; например,
set "v=a & b"
присваивает a & b
переменной буквальное значение %v%
(наоборот, set v="a & b"
делает двойные кавычки частью значения). Экранировать %
экземпляры литералов как %%
(работает только в пакетных файлах - см. Выше).
- Справка : ссылки на переменные в двойных кавычках, чтобы их значение не интерполировалось; например,
echo "%v%"
не подвергает значение %v%
интерполяции и печати "a & b"
(но обратите внимание, что двойные кавычки тоже неизменно печатаются). В противоположности этому , echo %v%
передает буквальное a
To echo
, интерпретируют &
как оператор командную секвенировании, и поэтому пытается выполнить команду с именем b
.
Также обратите внимание на приведенное выше предостережение о повторном использовании ^
с call
заявлением.
- Внешние программы обычно заботятся об удалении заключительных двойных кавычек вокруг параметров, но, как уже отмечалось, в пакетных файлах вы должны сделать это самостоятельно (например,
%~1
чтобы удалить заключительные двойные кавычки из 1-го параметра), и, к сожалению, нет прямого известный мне способ echo
точной печати значения переменной без двойных кавычек .
- Нил предлагает в
for
-На обходного пути , который работает до тех пор , пока значение не имеет встроенные двойные кавычки ; например:
set "var=^&')|;,%!"
for /f "delims=" %%v in ("%var%") do echo %%~v
cmd.exe
вовсе не признают одиночные -quotes , как разделители строк - они рассматриваются как литералы и обычно не могут быть использованы для определения строк с вложенными пробелами; кроме того, из этого следует, что токены, примыкающие к одинарным кавычкам, и любые токены между ними обрабатываются как не заключенные в кавычки cmd.exe
и интерпретируются соответственно.
- Однако, учитывая, что целевые программы в конечном итоге выполняют свой собственный синтаксический анализ аргументов, некоторые программы, такие как Ruby, действительно распознают строки в одинарных кавычках даже в Windows; Напротив, исполняемые файлы C / C ++, Perl и Python их не распознают.
Однако даже если это поддерживается целевой программой, не рекомендуется использовать строки в одинарных кавычках, поскольку их содержимое не защищено от потенциально нежелательной интерпретации с помощью cmd.exe
.
Цитата изнутри PowerShell:
Windows PowerShell - это гораздо более продвинутая оболочка, чем Windowscmd.exe
, и она уже много лет является частью Windows (и PowerShell Core привнес возможности PowerShell в macOS и Linux).
PowerShell внутренне согласованно работает с цитированием:
- внутри строк с двойными кавычками используйте
`"
или""
чтобы избежать двойных кавычек
- внутри строк
''
в одинарных кавычках используйте, чтобы избежать одинарных кавычек
Это работает в командной строке PowerShell и при передаче параметров в сценарии PowerShell или функции изнутри. PowerShell.
(Как обсуждалось выше, передача экранированных двойных кавычек в PowerShell извне требует \"
или, что более надежно,\""
ничего не работает).
К сожалению, при вызове внешних программ из PowerShell вы сталкиваетесь с необходимостью как учесть собственные правила цитирования PowerShell, так и уйти от целевой программы:
Это проблемное поведение также обсуждается и резюмируется в этом ответе.
Двойные кавычки внутри строк, заключенных в двойные кавычки :
Рассмотрим строку "3`" of rain"
, которая внутри PowerShell переводится в буквальный 3" of rain
.
Если вы хотите передать эту строку во внешнюю программу, вы должны применить экранирование целевой программы в дополнение к PowerShell ; скажем, вы хотите передать строку программе C, которая ожидает, что встроенные двойные кавычки будут экранированы как \"
:
foo.exe "3\`" of rain"
Обратите внимание, как должны присутствовать и то, и другое `"
- чтобы сделать PowerShell счастливым, и\
- чтобы сделать целевую программу счастливой.
Та же логика применяется к вызову командного файла, где ""
должны использоваться:
foo.bat "3`"`" of rain"
Напротив, встраивание одинарных кавычек в строку с двойными кавычками вообще не требует экранирования.
Одиночное -quotes внутри отдельных -quoted строк ничего не требует дополнительного побега; рассмотрим'2'' of snow'
, что является представлением PowerShell2' of snow
.
foo.exe '2'' of snow'
foo.bat '2'' of snow'
PowerShell переводит строки с одинарными кавычками в двойные кавычки перед их передачей в целевую программу.
Однако двойные кавычки внутри одинарных кавычек , которые не нужно экранировать для PowerShell , все же нужно экранировать для целевой программы :
foo.exe '3\" of rain'
foo.bat '3"" of rain'
PowerShell v3 представил магический --%
вариант , называемый стоп-разбор символом , который облегчает некоторые боли, пропускание ничего после того, как он необработанный к целевой программе, за исключением cmd.exe
-Style среды переменной ссылки (например, %USERNAME%
), которые будут расширены; например:
foo.exe --% "3\" of rain" -u %USERNAME%
Обратите внимание, что экранирования встроенного кода "
только \"
для целевой программы (а не для PowerShell as \`"
) достаточно.
Однако такой подход:
- не позволяет экранировать
%
символы, чтобы избежать раскрытия переменных среды.
- исключает прямое использование переменных и выражений PowerShell; вместо этого командная строка должна быть построена в строковой переменной на первом шаге, а затем вызвана с
Invoke-Expression
помощью на втором.
Таким образом, несмотря на многочисленные усовершенствования, PowerShell не упростил экранирование при вызове внешних программ. Тем не менее, он ввел поддержку строк в одинарных кавычках.
Интересно, возможно ли вообще в мире Windows когда-либо переключиться на модель Unix, позволяющую оболочке выполнять всю токенизацию и удаление цитат предсказуемо , заранее , независимо от целевой программы , а затем вызывать целевую программу, передавая полученные токены .