Избегание двойных кавычек в пакетном скрипте

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

@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
  • 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"

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

Одиночные кавычки внутри одинарных кавычек не требуют дополнительного экранирования; рассматривать '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

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

Другие вопросы по тегам