Вопрос

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

@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.

Цитата из в пределах 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;бросить' "-бежать

Надеюсь, это кому-то поможет, у меня сработало.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top