Экранирование двойных кавычек в пакетном скрипте

Как я могу заменить все двойные кавычки в параметрах моего пакетного файла на экранированные двойные кавычки? Это мой текущий пакетный файл, который расширяет все параметры командной строки внутри строки:

@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 to echo, интерпретирует & как оператор последовательности команд, и поэтому пытается выполнить команду с именем b.
          Также обратите внимание на выше предостережение повторно использовать ^ С call заявление.
        • внешний программы обычно заботятся об удалении вложенных двойных кавычек вокруг параметров, но, как уже отмечалось, в пакетных файлах вы должны сделать это сами (например, %~1 чтобы удалить заключительные двойные кавычки из 1-й параметр) и, к сожалению, нет прямого способа, который я знаю, чтобы получить echo чтобы точно напечатать значение переменной без заключать в двойные кавычки.
          • Нил предложения a for-на основе обходного пути, который работает пока значение не имеет встроенных двойных кавычек; например:
            set "var=^&')|;,%!" for /f "delims=" %%v in ("%var%") do echo %%~v
      • cmd.exe тут не узнать одинкавычки в качестве разделителей строк-они рассматриваются как литералы и обычно не могут использоваться для разграничения строк со встроенными пробелами; кроме того, из этого следует, что маркеры, примыкающие к одинарным кавычкам, и любые маркеры между ними рассматриваются как неквотируемые cmd.exe и интерпретировать соответственно.

        • однако, учитывая, что целевые программы в конечном итоге выполняют собственный синтаксический анализ аргументов, некоторые программы, такие как Ruby, распознают строки с одинарными кавычками даже в Windows; напротив, исполняемые файлы C/C++, Perl и Python do не распознать их.
          Однако даже при поддержке целевой программы не рекомендуется использовать строки с одинарными кавычками, поскольку их содержимое не защищено от потенциально нежелательной интерпретации cmd.exe.

      PowerShell

      Windows PowerShell гораздо более продвинутая оболочка, чем cmd.exe, и он был частью Windows в течение многих лет (и PowerShell Core принес опыт PowerShell в macOS и Linux, а также).

      PowerShell, который стабильно работает внутри в отношении цитирования:

      • внутри строки с двойными кавычками, используйте `" или "" чтобы избежать двойных кавычек
      • внутри строк с одинарными кавычками используйте '' чтобы избежать одинарных кавычек

      это работает в командной строке PowerShell и при передаче параметров в PowerShell скрипты или функции из внутри PowerShell.

      (как обсуждалось выше, передача экранированной двойной кавычки в PowerShell извне требует \" или, более робастно, \"" - ничего не работает).

      к сожалению, при вызове внешний программы, вы столкнулись с необходимостью размещения собственных правил цитирования PowerShell и бежать к цель:

      это проблемное поведение также обсуждается и обобщается в это GitHub документы выпуск

      двойной-кавычки внутри двойной-процитировал строки:

      рассмотрим строку "3`" of rain", который PowerShell-внутренне переводится на literal 3" of rain.

      если вы хотите передать эту строку во внешнюю программу, вы должны применить escaping целевой программы кроме того в PowerShell; скажем, вы хотите передать строку в C программа, которая ожидает, что встроенные двойные кавычки будут экранированы как \":

      foo.exe "3\`" of rain"
      

      обратите внимание, как и `" - чтобы сделать PowerShell счастливым -и на \ - чтобы сделать целевую программу счастливой - должна присутствовать.

      та же логика применяется к вызову пакетного файла, где "" необходимо:

      foo.bat "3`"`" of rain"
      

      наоборот, встраивание один-цитаты в a двойнойкавычки требует никакого побега вообще.

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