Избегание двойных кавычек в пакетном скрипте
Как мне заменить все двойные кавычки в параметрах моего пакетного файла на экранированные двойные кавычки? Это мой текущий командный файл, который раскрывает все параметры командной строки внутри строки:
@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:\Users\Me\Documents\Testing\SparseLib\bin\Win32\LinuxRelease\hello.o"
"c:\Users\Me\Documents\Testing\SparseLib\SparseLib\hello.cpp"
Когда первая кавычка вокруг первого переданного пути преждевременно заканчивает строку, передаваемую в GCC, и передает остальные параметры непосредственно в bash (что потрясающе не удается).
Я полагаю, что если я смогу объединить параметры в одну строку, а затем экранировать кавычки, это должно работать нормально, но мне трудно определить, как это сделать. Кто-нибудь знает?
7 ответов
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%"
Который заменяет все экземпляры "
с \"
улетел за баш.
Экранирующий символ в пакетных сценариях ^
, Но для двойных кавычек, удвойте кавычки:
"string with an embedded "" character"
Собственный ответ eplawless просто и эффективно решает его конкретную проблему: он заменяет все "
экземпляры во всем списке аргументов с \"
Это то, как Bash требует двойных кавычек внутри строки в двойных кавычках для представления.
Чтобы вообще ответить на вопрос о том, как избежать двойных кавычек внутри строки в двойных кавычках, используя cmd.exe
интерпретатор командной строки Windows (будь то в командной строке - часто все еще ошибочно называемый "приглашением DOS" - или в пакетном файле): см. внизу для просмотра PowerShell.
тл; др:
Вы должны использовать
""
при передаче строки в (другой) пакетный файл, и вы можете использовать""
с приложениями, созданными с помощью компиляторов Microsoft C/C++/.NET (которые также принимают\"
), который в Windows включает Python и Node.js:Пример:
foo.bat "We had 3"" of rain."
Следующее относится только к пакетным файлам:
""
это единственный способ получить командный интерпретатор (cmd.exe
) обрабатывать всю строку в двойных кавычках как один аргумент.К сожалению, однако, не только сохраняются вмещающие двойные кавычки (как обычно), но и удваиваются экранированные, поэтому получение намеченной строки является двухэтапным процессом; например, предполагая, что строка в двойных кавычках передается в качестве 1-го аргумента,
%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 в Windows,
\""
надежная альтернатива
Если вы должны использовать
\"
Есть только 3 безопасных подхода, которые, тем не менее, довольно обременительны: Кончик шляпы T S за его помощь.Используя (возможно выборочное) задержанное расширение переменной в вашем пакетном файле, вы можете хранить литералы
\"
в переменной и ссылаться на эту переменную внутри"..."
использование строки!var!
синтаксис - см . полезный ответ TS.- Вышеуказанный подход, несмотря на громоздкость, имеет то преимущество, что вы можете применять его методично и работать надежно с любым вводом.
Только с ЛИТЕРАЛЬНЫМИ строками - теми, которые НЕ включают ПЕРЕМЕННЫЕ - вы получаете такой же методический подход: категорически
^
-брось всеcmd.exe
метасимволы:" & | < >
и - если вы также хотите подавить расширение переменной -%
:foo.exe ^"3\^" of snow^" ^"^& ver.^"
В противном случае вы должны сформулировать свою строку на основе определения того, какие части строки
cmd.exe
считает не цитируемым из-за неправильной интерпретации\"
как закрывающие разделители:в буквальных частях, содержащих метасимволы оболочки:
^
-брось их; используя пример выше, это&
это должно быть^
убежал:foo.exe "3\" of snow" "^& ver."
порциями с
%...%
-стиль ссылки на переменные: убедитесь, чтоcmd.exe
считает их частью"..."
строка и то, что значения переменных сами не имеют встроенных, несбалансированных кавычек - что даже не всегда возможно.
Для справочной информации читайте дальше.
Фон
Примечание: это основано на моих собственных экспериментах. Дай мне знать, если я ошибаюсь.
POSIX-подобные оболочки, такие как Bash, в Unix-подобных системах маркируют список аргументов (строку) перед передачей аргументов индивидуально целевой программе: среди других расширений они разбивают список аргументов на отдельные слова (разбиение слов) и удаляют символы кавычек из результирующие слова (удаление цитаты). То, что передается целевой программе, это концептуально массив отдельных аргументов с удаленными (требующими синтаксиса) кавычками.
В отличие от этого интерпретатор команд Windows, по-видимому, не разбивает список аргументов на категории и просто передает единственную строку, содержащую все аргументы, включая символы кавычек. - к целевой программе.
Однако некоторая предварительная обработка выполняется до того, как одна строка передается целевой программе: ^
избежать символов. за пределами строк в двойных кавычках удаляются (они экранируют следующий символ.) и ссылки на переменные (например, %USERNAME%
) интерполируются первыми.
Таким образом, в отличие от Unix, целевая программа должна проанализировать, чтобы проанализировать строку аргументов и разбить ее на отдельные аргументы с удаленными кавычками. Таким образом, разные программы могут гипотетически требовать разные методы экранирования, и нет единого механизма экранирования, который гарантированно работал бы со всеми программами - /questions/21986216/kak-interpretator-komand-windows-cmdexe-analiziruet-stsenarii/21986221#21986221 содержит отличную информацию об анархии, которая является командной строкой 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, когда вызывается извне - например, из cmd.exe
из командной строки или из командного файла - распознает только \"
и в Windows более надежный \""
даже если внутренне PowerShell использует `
в качестве escape-символа в двойных кавычках, а также принимает ""
; например:
powershell -c " \"ab c\".length"
работает (выводы4
), как и более надежныйpowershell -c " \""ab c\"".length"
,но
powershell -c " ""ab c"".length"
перерывы
Связанная информация
^
может использоваться только как 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.
- ПРЕДУПРЕЖДЕНИЕ: использование
Спасаясь от буквального
%
к сожалению, это особый случай, который требует особого синтаксиса в зависимости от того, указана ли строка в командной строке, а не внутри командного файла; см. /questions/40979428/ignorirovat-znak-protsenta-v-paketnom-fajle/40979436#40979436- Суть этого: внутри командного файла используйте
%%
, В командной строке%
невозможно избежать, но если вы поместите^
в начале, конце или внутри имени переменной в строке без кавычек (например,echo %^foo%
), вы можете предотвратить расширение переменной (интерполяция);%
экземпляры в командной строке, которые не являются частью ссылки на переменную, обрабатываются как литералы (например,100%
).
- Суть этого: внутри командного файла используйте
Как правило, для безопасной работы со значениями переменных, которые могут содержать пробелы и специальные символы:
- Назначение: заключите имя переменной и ее значение в одну пару двойных кавычек; например,
set "v=a & b"
присваивает буквальное значениеa & b
к переменной%v%
(в отличие отset v="a & b"
сделает двойные кавычки частью значения). Побег буквальный%
случаи как%%
(работает только в пакетных файлах - см. выше). - Ссылка: двойные кавычки для ссылок на переменные, чтобы убедиться, что их значение не интерполировано; например,
echo "%v%"
не подлежит значению%v%
для интерполяции и печати"a & b"
(но учтите, что двойные кавычки тоже всегда печатаются). В отличие отecho %v%
проходит буквальноa
вecho
интерпретирует&
в качестве оператора последовательности команд, и поэтому пытается выполнить команду с именемb
,
Также обратите внимание на приведенное выше предостережение о повторном использовании^
сcall
заявление. - Внешние программы обычно заботятся об удалении заключенных в кавычки параметров, но, как отмечалось, в пакетных файлах вы должны сделать это самостоятельно (например,
%~1
удалить двойные кавычки из 1-го параметра) и, к сожалению, я не знаю прямого способа получитьecho
точно печатать значение переменной без заключенных в кавычки.- Neil предлагает
for
-обходное решение, которое работает до тех пор, пока значение не имеет двойных кавычек; например:set "var=^&')|;,%!" for /f "delims=" %%v in ("%var%") do echo %%~v
- Neil предлагает
- Назначение: заключите имя переменной и ее значение в одну пару двойных кавычек; например,
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 Core перенес PowerShell в MacOS и Linux).
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
-стиль ссылки на переменные среды (например, %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%
может содержать несопоставимые кавычки.
В Windows 10 21H1.
Если из партии (
.bat
) файл Я хочу запустить приложение Everything, я использую
"""
внутри аргумента двойных кавычек:
"C:\Program Files\Everything\Everything.exe" -search "<"""D:\My spaced folder""" | """Z:\My_non_spaced_folder"""> <*.jpg | *.jpeg | *.avi | *.mp4>"
Если строка уже заключена в кавычки, используйте другую кавычку, чтобы аннулировать ее действие.
echo "Insert tablename(col1) Values('""val1""')"
Например, для Unreal Engine Automation инструмент запускается из командного файла - это сработало для меня
Например: -cmdline=" -Messaging" -device=device -addcmdline="-SessionId=session -SessionOwner='owner' -SessionName='Build' -dataProviderMode=local -LogCmds='LogCommodity OFF' -execcmds='список автоматизации; тесты выполнения + разделены + +T1+T2; выход '' -run
Надеюсь, это поможет кому-то, работал на меня.