Избегание двойных кавычек в пакетном скрипте - PullRequest
78 голосов
/ 18 февраля 2009

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

@echo off
call bash --verbose -c "g++-linux-4.1 %*"

Затем эта строка используется для вызова Cygwin 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 (что не дает впечатляющего результата).

Я полагаю, что если я смогу объединить параметры в одну строку, то избежать кавычек, это должно работать нормально, но мне трудно определить, как это сделать. Кто-нибудь знает?

Ответы [ 5 ]

91 голосов
/ 18 февраля 2009

Экранирующий символ в пакетных сценариях - ^. Но для строк в двойных кавычках удвойте кавычки:

"string with an embedded "" character"
73 голосов
/ 14 июля 2015

собственный ответ 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 безопасных подходов , которые, однако, довольно громоздки : Совет шляпы TS за помощь.

  • Используя (возможно выборочное ) расширение отложенной переменной в вашем пакетном файле, вы можете сохранить литерал \" в переменной и ссылаться на эту переменную внутри строки "...", используя синтаксис !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 , когда вызывается извне - например, из cmd.exe, из командной строки или из пакетного файла - распознает только \" и в Windows более надежный \"", хотя внутренне PowerShell использует ` в качестве escape-символа в строках в двойных кавычках и также принимает ""; например:

  • powershell -c " \"ab c\".length" работает (вывод 4), как и более устойчивый
    powershell -c " \""ab c\"".length"

  • но powershell -c " ""ab c"".length" перерывы .


Дополнительная информация

  • ^ может использоваться только как escape-символ в без кавычек строк - внутри строк в двойных кавычках ^ не является специальным и трактуется как литерал .

    • CAVEAT : Использование ^ в параметрах, передаваемых в оператор 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, чтобы удалить заключающие в кавычки из 1-й параметр) и, к сожалению, я не знаю прямого способа получить echo для точного вывода значения переменной без с двойными кавычками .
      • Neil предлагает обходной путь на for, который работает , пока значение не имеет встроенных двойных кавычек ; e.g.:
        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 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"

В отличие от этого, для вставки одинарных кавычек в двойную строку в кавычках вообще не требуется экранирование.

Одиночные - кавычки внутри одинарные строки в кавычках do not требуют дополнительные экранирование; рассмотрим '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%), которые расширены ; e.g.:

foo.exe --% "3\" of rain" -u %USERNAME%

Обратите внимание на то, что экранирования встроенного " как \" только для целевой программы (и не только для PowerShell, как \`") достаточно.

Однако этот подход:

  • не позволяет экранировать % символов, чтобы избежать раскрытия переменных среды.
  • исключает прямое использование переменных и выражений PowerShell; вместо этого командная строка должна быть встроена в строковую переменную на первом шаге, а затем вызываться с Invoke-Expression во втором.

Таким образом, несмотря на многочисленные усовершенствования, PowerShell не сильно облегчает экранирование при вызове внешних программ. Однако он ввел поддержку строк в одинарных кавычках.

Интересно, возможно ли в мире Windows когда-либо принципиально переключиться на модель Unix, позволяющую оболочке 1718 * делать весь токенизацию и удаление цитат предсказуемо , заранее? , независимо от целевой программы , а затем вызвать целевую программу, передав получившиеся токены.

22 голосов
/ 18 февраля 2009

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.

8 голосов
/ 25 мая 2017

Как дополнение к отличному ответу 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

Эта замена небезопасна с расширением %...%!

В случае OP bash -c "g++-linux-4.1 !v_params:"=\"!" является безопасной версией.


Если по какой-то причине даже временное включение DELAYEDEXPANSION не является опцией, читайте дальше:

Использование \" изнутри cmd немного безопаснее, если всегда требуется экранировать специальные символы, а не просто иногда. (Менее вероятно, забыть карету, если она последовательна ...)

Для этого перед любой кавычкой следует ставить каретку (^"), кавычки, которые должны достигнуть дочернего процесса, так как литералы должны дополнительно экранироваться с обратной реакцией (\^"). ALL метасимволы оболочки также должны быть экранированы с ^, например, & => ^&; | => ^|; > => ^>; и т.д.

* +1054 * Пример:
child ^"malicious argument\^"^&whoami^"

Источник: Каждый цитирует аргументы командной строки неверным образом , см. "Лучший способ цитирования"


Для передачи динамического содержимого необходимо обеспечить следующее:
Часть команды, которая содержит переменную, должна рассматриваться как "заключенная в кавычки" cmd.exe (Это невозможно, если переменная может содержать кавычки - не записывать %var:""=\"%). Чтобы достичь этого, последние " перед переменной и первые " после переменной не ^ -экранируются. Метасимволы cmd между этими двумя " не должны быть экранированы. Пример:

foo.exe ^"danger ^& bar=\"region with %dynamic_content% & danger\"^"

Это не безопасно, если %dynamic_content% может содержать несопоставимые кавычки.

0 голосов
/ 03 декабря 2018

Например, для Unreal Engine Automation инструмент запускается из командного файла - это сработало для меня

например: -cmdline = "-Messaging" -device = device -addcmdline = "- SessionId = session -SessionOwner = 'owner' -SessionName = 'Build' -dataProviderMode = local -LogCmds = 'LogCommodity OFF' -execcmds = 'список автоматизации; runtests тесты + разделенные + + T1 + T2; выход '' -run

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

...