Экранирование двойных кавычек в пакетном скрипте
-
05-09-2019 - |
Вопрос
Как мне заменить все двойные кавычки в параметрах моего командного файла экранированными двойными кавычками?Это мой текущий пакетный файл, который расширяет все параметры командной строки внутри строки:
@echo off
call bash --verbose -c "g++-linux-4.1 %*"
Затем он использует эту строку для вызова bash Cygwin, выполняя кросс-компилятор Linux.К сожалению, я получаю такие параметры, которые передаются в мой командный файл:
"launch-linux-g++.bat" -ftemplate-depth-128 -O3 -finline-functions
-Wno-inline -Wall -DNDEBUG -c
-o "C:\Users\Me\Documents\Testing\SparseLib\bin\Win32\LinuxRelease\hello.o"
"c:\Users\Me\Documents\Testing\SparseLib\SparseLib\hello.cpp"
Если первая кавычка вокруг первого переданного пути преждевременно завершает строку, передаваемую в GCC, и передает остальные параметры непосредственно в bash (что приводит к впечатляющему сбою).
Я предполагаю, что если я смогу объединить параметры в одну строку, а затем избежать кавычек, все должно работать нормально, но мне трудно определить, как это сделать.Кто-нибудь знает?
Решение 3
В конце концов Google нашел ответ.Синтаксис пакетной замены строк следующий:
set v_myvar=replace me
set v_myvar=%v_myvar:ace=icate%
Который производит «копируй меня».Мой скрипт теперь выглядит так:
@echo off
set v_params=%*
set v_params=%v_params:"=\"%
call bash -c "g++-linux-4.1 %v_params%"
Который заменяет все экземпляры "
с \"
, правильно экранированный для bash.
Другие советы
Escape-символ в пакетных сценариях: ^
.Но для строк в двойных кавычках удвойте кавычки:
"string with an embedded "" character"
собственный ответ eplawless просто и эффективно решает свою конкретную задачу:он заменяет все "
экземпляры во всем списке аргументов с помощью \"
, именно так Bash требует для представления строки в двойных кавычках двойные кавычки.
Чтобы в целом ответить на вопрос как избежать двойных кавычек внутри строки в двойных кавычках, используя cmd.exe
, интерпретатор командной строки Windows (будь то в командной строке, которую часто ошибочно называют «приглашением DOS», или в пакетном файле):Смотрите внизу, чтобы посмотреть PowerShell.
вр; доктор:
Ты должен использовать
""
при передаче строки другому) командный файл а ты может использовать""
с приложениями, созданными с помощью Майкрософткомпиляторы C/C++/.NET (который также принимать\"
), который в Windows включает Python и Node.js:Пример:
foo.bat "We had 3"" of rain."
Следующее применимо только к пакетным файлам:
""
это единственный способ получить интерпретатор команд (cmd.exe
), чтобы рассматривать всю строку в двойных кавычках как одинокий аргумент.К сожалению, однако, сохраняются не только закрывающие двойные кавычки (как обычно), но и двойные экранированные кавычки, поэтому получение намеченной строки представляет собой двухэтапный процесс;например, если предположить, что строка в двойных кавычках передается в качестве первого аргумента,
%1
:set "str=%~1"
удаляет заключающие двойные кавычки;set "str=%str:""="%"
затем преобразует двойные двойные кавычки в одинарные.
Обязательно используйте двойные кавычки вокруг частей присваивания, чтобы предотвратить нежелательную интерпретацию значений.
\"
является необходимый - как единственный вариант - многими другими программами, (например, Ruby, Perl и даже собственный PowerShell от Microsoft(!)), но ЕГО ИСПОЛЬЗОВАНИЕ НЕ БЕЗОПАСНО:\"
это то, что многие исполняемые файлы и интерпретаторы требовать - включая собственный PowerShell от Microsoft при передаче строк снаружи - или, в случае Компиляторы Microsoft, поддержка как альтернатива""
- в конце концов, однако, целевая программа должна проанализировать список аргументов.- Пример:
foo.exe "We had 3\" of rain."
- Пример:
- ОДНАКО, ИСПОЛЬЗОВАНИЕ
\"
МОЖЕТ ПРИВЕСТИ К НЕЖЕЛАТЕЛЬНОМУ, ПРОИЗВОЛЬНОМУ ВЫПОЛНЕНИЮ КОМАНД и/или ПЕРЕНАПРАВЛЕНИЮ ВВОДА/ВЫВОДА.:- Следующие символы представляют такой риск:
& | < >
- Например, следующее приводит к непреднамеренному выполнению
ver
команда;см. ниже объяснение и следующий пункт для обходного пути:foo.exe "3\" of snow" "& ver."
- Следующие символы представляют такой риск:
- Для PowerShell на Окна,
\""
и"^""
являются надежными, но ограниченными альтернативами (см. раздел «Вызов CLI PowerShell...» ниже).
Если вам необходимо использовать
\"
, их всего 3 безопасный подходы, которые, однако, довольно громоздкий: Наконечник шляпы, чтобы Т С за его помощь.Используя (возможно избирательный) расширение переменной с задержкой в вашем пакетном файле вы можете хранить литерал
\"
в переменная и ссылаться на эту переменную внутри"..."
строка с использованием!var!
синтаксис - видеть Полезный ответ TS.- Вышеописанный подход, несмотря на его громоздкость, имеет то преимущество, что его можно применить. методично и что это работает сильно, с любым входом.
Только с ЛИТЕРАЛЬНЫМИ строками, НЕ включающими ПЕРЕМЕННЫЕ, вы получаете аналогичный методический подход:категорически
^
-побег всеcmd.exe
метасимволы:" & | < >
и - если вы также хотите подавить расширение переменных -%
:
foo.exe ^"3\^" of snow^" ^"^& ver.^"
В противном случае вы должны сформулируйте свою строку, основываясь на распознавании того, какие части строки
cmd.exe
считает без кавычек из-за неправильного толкования\"
в качестве закрывающих разделителей:в буквальный части, содержащие метасимволы оболочки:
^
-убежать от них;используя приведенный выше пример, это&
это должно быть^
-сбежал:
foo.exe "3\" of snow" "^& ver."
порциями с
%...%
-стиль ссылок на переменные:гарантировать, что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 и даже собственный PowerShell от Microsoft при вызове из cmd.exe
- и, за исключением PowerShell с \""
, для них простого решения этой проблемы не существует.
По сути, вам нужно заранее знать, какие части вашей командной строки ошибочно интерпретируются как не заключенные в кавычки, и выборочно ^
-экранировать все экземпляры & | < >
в этих порциях.
Напротив, использование ""
безопасно, но это к сожалению, поддерживается только исполняемыми и пакетными файлами на основе компилятора Microsoft. (в случае пакетных файлов с описанными выше особенностями), что, в частности, исключает PowerShell - см. следующий раздел.
Вызов CLI PowerShell из cmd.exe
или POSIX-подобные оболочки:
Примечание:См. нижний раздел, чтобы узнать, как обрабатывается цитирование. внутри PowerShell.
PowerShell, при вызове снаружи - например, из cmd.exe
, будь то из командной строки или пакетного файла - признает только \"
и в Windows также """
и чем более крепкий \""
/ "^""
(Несмотря на то внутренне PowerShell использует `
в качестве escape-символа в строках в двойных кавычках, а также принимает ""
- см. нижний раздел):
На Окна, звоню от cmd.exe
/ пакетный файл:
""
перерывы, потому что он принципиально не поддерживается:powershell -c " ""ab c"".length "
-> ошибка «В строке отсутствует терминатор»
\"
и"""
работа в принципе, но это не так безопасный:powershell -c " \"ab c\".length "
работает как задумано:он выводит5
(Обратите внимание 2 пробелы)- Но это небезопасно, потому что
cmd.exe
метасимволы нарушают команду, если они не экранированы:
powershell -c " \"a& c\".length "
перерывы, из-за&
, который нужно было бы экранировать как^&
\""
является безопасный, но нормализовать внутренние пробелы, что может быть нежелательно:powershell -c " \""a& c\"".length "
результаты4
(!), потому что 2 пробела нормализованы к 1.
"^""
это лучший выбор для Windows PowerShell конкретно, где это безопасно и сохраняет пробелы, но с PowerShell Основной (в Windows) это то же самое, что и\""
, то есть пробелы-нормализация.Кредит идет на Венрикс за открытие этого подхода.powershell -c " "^""a& c"^"".length "
работает:не ломается - несмотря на&
- и результаты5
, т. е. правильно сохраненные пробелы.PowerShell Основной:
pwsh -c " "^""a& c"^"".length "
работает, но выводит4
, т.е. нормализует пробелы, как\""
делает.
На Unix-подобные платформы (Linux, macOS), вызов PowerShell ОсновнойCLI, pwsh
из POSIX-подобной оболочки, такой как bash
:
Ты должен использовать \"
, что, однако, одновременно безопасно и с сохранением пробелов:
$ pwsh -c " \"a& c|\".length" # OK: 5
Связанная информация
^
может использоваться только в качестве escape-символа в без кавычек струны - внутри строк в двойных кавычках,^
не является особенным и рассматривается как буквальный.- ПРЕДОСТЕРЕЖЕНИЕ: Использование
^
в параметрах, передаваемых в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- Краткое изложение:Внутри пакетного файла используйте
%%
.В командной строке%
нельзя избежать, но если вы поместите^
в начале, конце или внутри имени переменной в без кавычек строка (например,echo %^foo%
), вы можете предотвратить расширение переменных (интерполяцию);%
экземпляры в командной строке, которые не являются частью ссылки на переменную, рассматриваются как литералы (например,100%
).
- Краткое изложение:Внутри пакетного файла используйте
В целом, безопасно работать со значениями переменных, которые могут содержать пробелы и специальные символы:
- Назначение: Вложить оба имя переменной и значение в одинокий пара двойных кавычек;например.,
set "v=a & b"
присваивает буквальное значениеa & b
в переменную%v%
(напротив,set v="a & b"
сделает двойные кавычки частью значения).Escape-литерал%
случаи как%%
(работает только в пакетных файлах - см. выше). - Ссылка: Ссылки на переменные в двойных кавычках чтобы убедиться, что их значение не интерполировано;например.,
echo "%v%"
не влияет на стоимость%v%
для интерполяции и печати"a & b"
(но учтите, что двойные кавычки тоже всегда печатаются).Напротив,echo %v%
проходит буквальноa
кecho
, интерпретирует&
в качестве оператора последовательности команд и поэтому пытается выполнить команду с именемb
.
Также обратите внимание на приведенное выше предостережение относительно повторного использования^
сcall
заявление. - Внешний программы обычно заботятся об удалении параметров, заключенных в двойные кавычки, но, как уже отмечалось, в пакетных файлах вам придется делать это самостоятельно (например,
%~1
удалить двойные кавычки из первого параметра) и, к сожалению, я не знаю прямого способа получитьecho
правильно печатать значение переменной без заключающие двойные кавычки.- Нил предложения а
for
обходной путь, который работает пока значение не имеет встроенных двойных кавычек;например.:
set "var=^&')|;,%!" for /f "delims=" %%v in ("%var%") do echo %%~v
- Нил предложения а
- Назначение: Вложить оба имя переменной и значение в одинокий пара двойных кавычек;например.,
cmd.exe
делает нет распознавать одинокий-кавычки как разделители строк — они рассматриваются как литералы и обычно не могут использоваться для разделения строк со встроенными пробелами;кроме того, из этого следует, что токены, примыкающие к одинарным кавычкам, и любые токены между ними рассматриваются как не заключенные в кавычкиcmd.exe
и интерпретировано соответствующим образом.- Однако, учитывая, что целевые программы в конечном итоге выполняют собственный анализ аргументов, некоторые программы, такие как Ruby, распознают строки в одинарных кавычках даже в Windows;напротив, исполняемые файлы C/C++, Perl и Python нет распознать их.
Однако, даже если это поддерживается целевой программой, не рекомендуется использовать строки в одинарных кавычках, поскольку их содержимое не защищено от потенциально нежелательной интерпретацииcmd.exe
.
- Однако, учитывая, что целевые программы в конечном итоге выполняют собственный анализ аргументов, некоторые программы, такие как Ruby, распознают строки в одинарных кавычках даже в Windows;напротив, исполняемые файлы C/C++, Perl и Python нет распознать их.
Цитата из в пределах PowerShell:
Windows PowerShell это гораздо более продвинутая оболочка, чем cmd.exe
, и он уже много лет является частью Windows (и Ядро PowerShell также привнесли возможности PowerShell в macOS и Linux).
PowerShell работает стабильно внутренне что касается цитирования:
- внутри строк в двойных кавычках используйте
`"
или""
избежать двойных кавычек - внутри строк в одинарных кавычках используйте
''
чтобы избежать одинарных кавычек
Это работает в командной строке PowerShell и при передаче параметров в сценарии или функции PowerShell из в пределах PowerShell.
(Как обсуждалось выше, передача экранированной двойной кавычки в PowerShell снаружи требует \"
или, более устойчиво, \""
- больше ничего не работает).
К сожалению, при вызове внешний программ из PowerShell, вы сталкиваетесь с необходимостью учитывать собственные правила цитирования PowerShell. и сбежать ради цель программа:
Это проблемное поведение также обсуждается и резюмируется в эта проблема с документацией GitHub
Двойной-кавычки внутри двойной-строки в кавычках:
Рассмотрим строку "3`" of rain"
, что внутри PowerShell преобразуется в буквальный 3" of rain
.
Если вы хотите передать эту строку во внешнюю программу, вам нужно применить экранирование целевой программы кроме того в PowerShell;скажем, вы хотите передать строку в программу C, которая ожидает, что встроенные двойные кавычки будут экранированы как \"
:
foo.exe "3\`" of rain"
Обратите внимание, как оба `"
- чтобы сделать PowerShell счастливым - и тот \
- чтобы целевая программа была счастлива - должна присутствовать.
Та же логика применима и к вызову пакетного файла, где ""
должен быть использован:
foo.bat "3`"`" of rain"
Напротив, вложение одинокий-кавычки в двойной- строка в кавычках вообще не требует побега.
Одинокий-кавычки внутри одинокий-строки в кавычках делать нет требовать дополнительный побег;учитывать '2'' of snow'
, который представляет собой представление PowerShell 2' 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, поскольку \`"
) достаточно.
Однако этот подход:
- не позволяет побег
%
символы, чтобы избежать расширения переменных среды. - исключает прямой использование переменных и выражений PowerShell;вместо этого на первом этапе командная строка должна быть встроена в строковую переменную, а затем вызываться с помощью
Invoke-Expression
через секунду.
Таким образом, несмотря на многочисленные усовершенствования, PowerShell не значительно облегчил экранирование при вызове внешних программ.Однако появилась поддержка строк в одинарных кавычках.
Интересно, возможно ли в мире Windows когда-нибудь перейти на модель Unix, позволяющую оболочка выполните всю токенизацию и удаление цитат предсказуемо, впереди, независимо от целевой программы, а затем вызвать целевую программу, передав полученные токены.
В качестве дополнения к отличный ответ mklement0:
Почти все исполняемые файлы принимают \"
как сбежавший "
.Однако безопасное использование в cmd практически возможно только с использованием DELAYEDEXPANSION.
Чтобы явно отправить литерал "
какому-то процессу назначить \"
в переменную среды, а затем используйте эту переменную всякий раз, когда вам нужно передать кавычку.Пример:
SETLOCAL ENABLEDELAYEDEXPANSION
set q=\"
child "malicious argument!q!&whoami"
Примечание SETLOCAL ENABLEDELAYEDEXPANSION
кажется, работает только в пакетных файлах.Чтобы получить DELAYEDEXPANSION в интерактивном сеансе, запустите cmd /V:ON
.
Если ваш пакетный файл не работает с 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
Эта замена небезопасна с %...%
расширение стиля!
В случае ОП bash -c "g++-linux-4.1 !v_params:"=\"!"
это безопасная версия.
Если по какой-то причине даже временное включение DELAYEDEXPANSION невозможно, читайте дальше:
С использованием \"
изнутри cmd немного безопаснее, если нужно всегда экранировать специальные символы, а не только иногда.(Меньше вероятность забыть каретку, если она последовательна...)
Чтобы добиться этого, перед любой кавычкой ставится курсор (^"
), кавычки, которые должны достигать дочернего процесса как литералы, дополнительно должны быть экранированы обратной обратной связью (\^"
). ВСЕ Метасимволы оболочки должны быть экранированы с помощью ^
а также, например &
=> ^&
; |
=> ^|
; >
=> ^>
;и т. д.
Пример:
child ^"malicious argument\^"^&whoami^"
Источник: Все неправильно цитируют аргументы командной строки, см. «Лучший метод цитирования»
Для передачи динамического контента необходимо обеспечить следующее:
Часть команды, содержащая переменную, должна считаться «заключенной в кавычки» cmd.exe
(Это невозможно, если переменная может содержать кавычки – не пиши %var:""=\"%
).Для этого последний "
перед переменной и первым "
после того, как переменная не ^
- сбежал.cmd-метасимволы между этими двумя "
не следует избегать.Пример:
foo.exe ^"danger ^& bar=\"region with %dynamic_content% & danger\"^"
Это небезопасно, если %dynamic_content%
может содержать несовпадающие кавычки.
Например, для инструмента автоматизации движка Unreal, запускаемого из командного файла, это сработало для меня.
например:-cmdline=" -Messaging" -device=device -addcmdline="-SessionId=session -SessionOwner='owner' -SessionName='Build' -dataProviderMode=local -LogCmds='LogCommodity OFF' -execcmds='список автоматизации;runtests тесты+разделенные+by+T1+T2;бросить' "-бежать
Надеюсь, это кому-то поможет, у меня сработало.