Экранирование двойных кавычек в пакетном скрипте
Как я могу заменить все двойные кавычки в параметрах моего пакетного файла на экранированные двойные кавычки? Это мой текущий пакетный файл, который расширяет все параметры командной строки внутри строки:
@echo off
call bash --verbose -c "g++-linux-4.1 %*"
затем он использует эту строку для вызова cygwin's bash, выполняя кросс-компилятор Linux. К сожалению, я получаю такие параметры, которые передаются в мой пакетный файл:
"launch-linux-g++.bat" -ftemplate-depth-128 -O3 -finline-functions
-Wno-inline -Wall -DNDEBUG -c
-o "C:UsersMeDocumentsTestingSparseLibbinWin32LinuxReleasehello.o"
"c:UsersMeDocumentsTestingSparseLibSparseLibhello.cpp"
где прошла первая цитата вокруг первого пути in преждевременно завершает строку, передаваемую в GCC, и передает остальные параметры непосредственно bash (который терпит неудачу эффектно.)
Я предполагаю, что если я могу объединить параметры в одну строку, то избежать кавычек он должен работать нормально, но мне трудно определить, как это сделать. Кто-нибудь знает?
4 ответов
escape-символ в пакетных сценариях -^
. Но для строк с двойными кавычками удвоьте кавычки:
"string with an embedded "" character"
собственный ответ eplawless просто и эффективно решает свою конкретную проблему: заменяет все "
экземпляры во всем списке аргументов с \"
, именно так Bash требует, чтобы двойные кавычки внутри строки с двойными кавычками были представлены.
чтобы вообще ответить на вопрос как избежать двойных кавычек внутри строки с двойными кавычками с помощью cmd.exe
, интерпретатор командной строки Windows (будь то в командной строке - часто еще ошибочно называют "DOS prompt" - или в пакетном файле):смотрите внизу для просмотра PowerShell.
tl; dr:
-
вы должны использовать
""
при передаче строки a (nother)пакетным файлом и мая использовать""
с приложениями, созданными с Microsoftкомпиляторы C/C++/.NET (где и принимать\"
), который на Windows включает Python и Node.js:пример:
foo.bat "We had 3"" of rain."
-
следующее относится только к пакетным файлам:
-
""
- это единственный способ получить командный интерпретатор (cmd.exe
) для обработки всей строки с двойными кавычками как один, которые,довольно громоздкая: кончик шляпы к T S за его помощь.-
через (возможно,выборочная) переменная с задержкой расширение в вашем пакетном файле вы можете store literal
\"
на переменная и ссылаться на эту переменную внутри"..."
строку!var!
синтаксис - см. T S полезный ответ.- выше подход, несмотря на громоздкость, имеет то преимущество, что вы можете применить его методически и что он работает надежно, с любой вход.
только с ЛИТЕРАЛЬНЫМИ строками-теми, которые не включают переменные - вы получаете аналогичный методический подход: категорически
^
побег всеcmd.exe
метасимволы:" & | < >
и-если вы также хотите подавить расширение переменной -%
:foo.exe ^"3\^" of snow^" ^"^& ver.^"
-
в противном случае, вы должны сформулируйте свою строку на основе распознавания, какие части строка
cmd.exe
считает без кавычек из-за неправильного толкования\"
как закрытие разделители:на литерал части, содержащие метасимволы оболочки:
^
-избежать их; используя пример выше, это&
что нужно^
-сбежал:foo.exe "3\" of snow" "^& ver."
в части с
%...%
- ссылки на переменные стиля: обеспечитьcmd.exe
считает они часть a"..."
строка и что значения переменных сами по себе не имеют встроенных, несбалансированных кавычек - что даже не всегда возможно.
-
для получения справочной информации читайте дальше.
фон
Примечание: это основано на моих собственных экспериментов. Дайте мне знать, если я неправильный.
POSIX-подобные оболочки, такие как Bash в Unix-подобных системах, обозначают список аргументов (строку) перед передачей аргументов индивидуально для целевой программы: среди других расширений они разделяют список аргументов на отдельные слова (разделение слов) и удаляют символы цитирования из результирующих слов (удаление цитат). То, что передается целевой программе, концептуально представляет собой массив отдельных аргументов с (требуемыми синтаксисом) кавычками удаленный.
напротив, интерпретатор команд Windows, по-видимому, не токенизирует список аргументов и просто передает один строка, содержащая все аргументы-включая цитирование символов. - к целевой программе.
Однако,некоторые предварительная обработка происходит до передачи одной строки в целевую программу:^
escape chars. вне двойных кавычек строки удаляются (они избежать следующего char.), и ссылки на переменные (например,%USERNAME%
) составляют интерполированное первый.таким образом, в отличие от Unix, задача целевой программы-проанализировать строку аргументов и разбить ее на отдельные аргументы с удаленными кавычками. Таким образом, различные программы могут гипотетически требовать различных методов экранирования и нет ни одного механизма побега, который гарантированный для работа со всеми программами - https://stackoverflow.com/a/4094897/45375 содержит отличный фон для анархии, которая является синтаксическим анализом командной строки Windows.
на практике
\"
очень распространен, но не безопасен, как упоминалось выше:с
cmd.exe
сам не признает\"
как бежал double-quote, он может неправильно истолковать более поздние токены в командной строке как без кавычек и потенциально интерпретировать их как команды и/или перенаправление ввода/вывода.
в двух словах: Проблема поверхности, если любой из следующих символов следовать открытие или несбалансированное\"
:& | < >
, например:foo.exe "3\" of snow" "& ver."
cmd.exe
видит следующие маркеры, в результате неправильного толкования\"
регулярные двойная цитата:"3\"
of
snow" "
- остальное:
& ver.
с
cmd.exe
считает, что& ver.
is без кавычек, он интерпретирует его как&
(оператор последовательности команд), за которым следует имя команды для выполнения (ver.
- the.
игнорируется;ver
отчетыcmd.exe
информация о версии).
Общий эффект есть:- во-первых,
foo.exe
вызывается с первого 3 жетоны только. - затем команда
ver
выполняется.
даже в тех случаях, когда случайная команда не наносит вреда, ваша общая команда не будет работать так, как задумано, учитывая, что не все аргументы передаются ей.
многие компиляторы / интерпретаторы распознают только
\"
- например, компилятор GNU C / C++, Python, Perl, Ruby, даже собственный PowerShell Microsoft при вызове изcmd.exe
- и, за исключением PowerShell с\""
, их нет простого решения этой проблемы.
По сути, вы должны знать заранее, какие части вашей командной строки неправильно интерпретируются как некотируемые и выборочно^
-побег всех экземпляров& | < >
в этих порциях.напротив, использование
""
безопасное, но это к сожалению только поддерживается Microsoft-компилятор на основе исполняемых файлов и пакетных файлов (в случае пакетных файлов, с причудами см. выше).напротив, PowerShell, при обращении извне - например,
cmd.exe
, то ли из командной строки или пакетного файла - признает только\"
и, на Windows, более надежный\""
, хотя внутри PowerShell использует`
как escape-символ в строках с двойными кавычками, а также принимает""
, например:powershell -c " \"ab c\".length"
работает (выход4
), как и более надежныеpowershell -c " \""ab c\"".length"
,но
powershell -c " ""ab c"".length"
разрывы.
информация, связанная с
-
^
можно использовать только как символ в без кавычек строки - внутри строк с двойными кавычками,^
не является особенным и рассматривается как литерал.-
предостережение: использование
^
в параметрах, переданных вcall
заявление сломана (это относится к обоим видам использованияcall
: вызов другого пакетного файла или двоичного файла и вызов подпрограммы в том же пакетном файле):-
^
экземпляров в двойные кавычки значения необъяснимо два раза, изменяя передаваемое значение: например, если переменная%v%
содержит буквальное значениеa^b
,call :foo "%v%"
назначает"a^^b"
(!) to%1
(первый параметр) в подпрограмме:foo
. -
без кавычек использование
^
Сcall
is вообще сломанной в этой^
больше не может использоваться для избежания специальных символов: например,call foo.cmd a^&b
тихо ломается (вместо прохождения литералаa&b
слишкомfoo.cmd
, как было бы безcall
) -foo.cmd
никогда даже не вызывается(!), по крайней мере, на Windows 7.
-
-
предостережение: использование
-
Побег из буквального
%
- это особый случай, к сожалению, что требует отдельного синтаксиса в зависимости от того, указана ли строка в командная строка и внутри пакета файл; см. https://stackoverflow.com/a/31420292/45375- короче говоря: внутри пакетного файла используйте
%%
. В командной строке%
невозможно избежать, но если вы поместите^
в начале, конце или внутри имени переменной в без кавычек строку (например,echo %^foo%
), вы можете предотвратить переменное расширение (интерполяцию);%
экземпляры в командной строке, которые не являются частью ссылки на переменную рассматриваются как литералы (e.g,100%
).
- короче говоря: внутри пакетного файла используйте
-
как правило, безопасно работать со значениями переменных, которые могут содержать пробелы и специальные символы:
-
задание: заключите и в имя переменной и значение в элементе один пара двойных кавычек, например,
set "v=a & b"
назначает буквальное значениеa & b
переменная%v%
(by контраст,set v="a & b"
сделает двойные кавычки частью значения). Побег буквально%
экземпляров%%
(работает только в пакетных файлах - см. выше). -
ссылка: ссылки на переменные с двойной кавычкой чтобы убедиться, что их значение не интерполируется; например,
echo "%v%"
не подлежит значение%v%
для интерполяции и печати"a & b"
(но обратите внимание, что двойные кавычки также неизменно печатаются). Напротив,echo %v%
проходит буквальноa
toecho
, интерпретирует&
как оператор последовательности команд, и поэтому пытается выполнить команду с именемb
.
Также обратите внимание на выше предостережение повторно использовать^
Сcall
заявление. -
внешний программы обычно заботятся об удалении вложенных двойных кавычек вокруг параметров, но, как уже отмечалось, в пакетных файлах вы должны сделать это сами (например,
%~1
чтобы удалить заключительные двойные кавычки из 1-й параметр) и, к сожалению, нет прямого способа, который я знаю, чтобы получитьecho
чтобы точно напечатать значение переменной без заключать в двойные кавычки.-
Нил предложения a
for
-на основе обходного пути, который работает пока значение не имеет встроенных двойных кавычек; например:set "var=^&')|;,%!" for /f "delims=" %%v in ("%var%") do echo %%~v
-
Нил предложения a
-
задание: заключите и в имя переменной и значение в элементе один пара двойных кавычек, например,
-
cmd.exe
тут не узнать одинкавычки в качестве разделителей строк-они рассматриваются как литералы и обычно не могут использоваться для разграничения строк со встроенными пробелами; кроме того, из этого следует, что маркеры, примыкающие к одинарным кавычкам, и любые маркеры между ними рассматриваются как неквотируемыеcmd.exe
и интерпретировать соответственно.- однако, учитывая, что целевые программы в конечном итоге выполняют собственный синтаксический анализ аргументов, некоторые программы, такие как Ruby, распознают строки с одинарными кавычками даже в Windows; напротив, исполняемые файлы C/C++, Perl и Python do не распознать их.
Однако даже при поддержке целевой программы не рекомендуется использовать строки с одинарными кавычками, поскольку их содержимое не защищено от потенциально нежелательной интерпретацииcmd.exe
.
- однако, учитывая, что целевые программы в конечном итоге выполняют собственный синтаксический анализ аргументов, некоторые программы, такие как Ruby, распознают строки с одинарными кавычками даже в Windows; напротив, исполняемые файлы C/C++, Perl и Python do не распознать их.
PowerShell
Windows PowerShell гораздо более продвинутая оболочка, чем
cmd.exe
, и он был частью Windows в течение многих лет (и PowerShell Core принес опыт PowerShell в macOS и Linux, а также).PowerShell, который стабильно работает внутри в отношении цитирования:
- внутри строки с двойными кавычками, используйте
`"
или""
чтобы избежать двойных кавычек - внутри строк с одинарными кавычками используйте
''
чтобы избежать одинарных кавычек
это работает в командной строке PowerShell и при передаче параметров в PowerShell скрипты или функции из внутри PowerShell.
(как обсуждалось выше, передача экранированной двойной кавычки в PowerShell извне требует
\"
или, более робастно,\""
- ничего не работает).к сожалению, при вызове внешний программы, вы столкнулись с необходимостью размещения собственных правил цитирования PowerShell и бежать к цель:
это проблемное поведение также обсуждается и обобщается в это GitHub документы выпуск
двойной-кавычки внутри двойной-процитировал строки:
рассмотрим строку
"3`" of rain"
, который PowerShell-внутренне переводится на literal3" of rain
.если вы хотите передать эту строку во внешнюю программу, вы должны применить escaping целевой программы кроме того в PowerShell; скажем, вы хотите передать строку в C программа, которая ожидает, что встроенные двойные кавычки будут экранированы как
\"
:foo.exe "3\`" of rain"
обратите внимание, как и
`"
- чтобы сделать PowerShell счастливым -и на\
- чтобы сделать целевую программу счастливой - должна присутствовать.та же логика применяется к вызову пакетного файла, где
""
необходимо:foo.bat "3`"`" of rain"
наоборот, встраивание один-цитаты в a двойнойкавычки требует никакого побега вообще.
один-кавычки внутри один-процитировал строки do не требуются extra побеге; рассмотреть
'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 введена магия
--%
опции, назвал стоп-анализ символов, который снимает часть боли, передавая что-нибудь после него uninterpreted в целевую программу, сохранить дляcmd.exe
-стиль окружающей среды-ссылки на переменные (например,%USERNAME%
), которая are расширенная; например:foo.exe --% "3\" of rain" -u %USERNAME%
обратите внимание, как избежать врезанный
"
as\"
только для целевой программы (а не Также для PowerShell как\`"
) является достаточным.однако, этот подход:
- не допускает побег
%
символы, чтобы избежать расширения переменных среды. - запрещает прямые использование переменных и выражений PowerShell; вместо этого командная строка должна быть построена в строковой переменной на первом шаге, а затем вызвана с помощью
Invoke-Expression
в секунду.
таким образом, несмотря на свои многочисленные достижения, PowerShell не сделал побег намного проще при вызове внешних программ. Это, однако, введена поддержка один-в кавычках.
интересно, возможно ли в мире Windows когда-либо переключиться на модель Unix, позволяющую shell сделайте все токенизация и удаление цитаты как и ожидалось, спереди, независимо от целевой программы, а затем вызовите целевую программу, передав полученные токены.
-
Google в конце концов придумал ответ. Синтаксис замены строк в пакете следующий:
set v_myvar=replace me
set v_myvar=%v_myvar:ace=icate%
который производит "replicate me". Мой скрипт теперь выглядит так:
@echo off
set v_params=%*
set v_params=%v_params:"=\"%
call bash -c "g++-linux-4.1 %v_params%"
который заменяет все экземпляры "
С \"
, правильно сбежал для bash.
в дополнение к отличный ответ mklement0:
почти все исполняемые файлы принимать \"
как сбежавший "
. Безопасное использование в cmd, однако, почти возможно только с помощью DELAYEDEXPANSION.
Чтобы явно отправить литерал "
для некоторого процесса назначьте \"
к переменной среды, а затем используйте эту переменную, когда вам нужно передать цитату. Пример:
SETLOCAL ENABLEDELAYEDEXPANSION
set q=\"
child "malicious argument!q!&whoami"
Примечание SETLOCAL ENABLEDELAYEDEXPANSION
кажется, работает только в пакетных файлах. К получите DELAYEDEXPANSION в интерактивном сеансе, запустите cmd /V:ON
.
если ваш batchfile не работает с DELAYEDEXPANSION, вы можете включить его временно:
::region without DELAYEDEXPANSION
SETLOCAL ENABLEDELAYEDEXPANSION
::region with DELAYEDEXPANSION
set q=\"
echoarg.exe "ab !q! & echo danger"
ENDLOCAL
::region without DELAYEDEXPANSION
если вы хотите передать динамическое содержимое из переменной, содержащей кавычки, которые экранируются как ""
вы можете заменить ""
С \"
расширения:
SETLOCAL ENABLEDELAYEDEXPANSION
foo.exe "danger & bar=region with !dynamic_content:""=\"! & danger"
ENDLOCAL
эта замена не является безопасным с %...%
стиль расширения!
в случае OP bash -c "g++-linux-4.1 !v_params:"=\"!"
- это безопасный вариант.
если по какой-то причине даже временное включение DELAYEDEXPANSION не является опцией, читайте дальше:
используя \"
изнутри cmd немного безопаснее, если всегда нужно избегать специальных символов, а не просто иногда. (Это менее вероятно, чтобы забыть каретку, если она последовательна...)
для достижения этого, один предшествует любой цитате с кареткой (^"
), котировки, которые должны достигать дочерний процесс как литералы должен быть дополнительно экранирован с отрицательной реакцией (\^"
). все метасимволы оболочки должны быть экранированы с помощью ^
, например &
=>^&
; |
=>^|
; >
=>^>
; etc.
пример:
child ^"malicious argument\^"^&whoami^"
источник: все цитируют аргументы командной строки неправильно, см. "лучший метод цитирования"
для передачи динамического контента, необходимо обеспечить следующее:
Часть команды, которая содержит переменную, должна считаться "цитируемой"cmd.exe
(это невозможно, если переменная может содержать кавычки - не пиши %var:""=\"%
). Чтобы достичь этого, последний "
перед переменной и первый "
после переменной не являются ^
-сбежал. КМД-метасимволы между этими двумя "
не должен быть спасен. Пример:
foo.exe ^"danger ^& bar=\"region with %dynamic_content% & danger\"^"
это небезопасно, если %dynamic_content%
может содержать непревзойденные цитаты.