Собственный ответ 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%передает буквальное aTo 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, позволяющую оболочке выполнять всю токенизацию и удаление цитат предсказуемо , заранее , независимо от целевой программы , а затем вызывать целевую программу, передавая полученные токены .