在批处理脚本中转义双引号

我将如何去replace我的batch file的参数中的所有双引号转义双引号? 这是我目前的batch file,它扩展了string中的所有命令行参数:

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

然后使用该string来调用Cygwin的bash,执行一个Linux交叉编译器。 不幸的是,我得到这样的parameter passing给我的batch file:

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

在传入的第一个path周围的第一个引号过早地结束了传递给GCC的string,并将其余的参数直接传递给bash(这会令人惊叹)。

我想如果我可以连接参数到一个单一的string然后转义报价应该工作正常,但我很难确定如何做到这一点。 有人知道吗?

批处理脚本中的转义字符是^ 。 但对于双引号的string,加双引号:

 "string with an embedded "" character" 

eplawless自己的答案简单而有效地解决了他的具体问题:它将整个参数列表中的所有"实例replace为\" ,这就是Bash在双引号string中需要用双引号表示的情况。

为了普遍地回答如何使用cmd.exe在双引号string中转义双引号的问题,Windows命令行解释程序(无论是在命令行上 – 通常还是错误地称为“DOS提示符” – 或者batch file): 查看底部查看PowerShell

tl; dr

  • 必须在将string传递给(另一个)batch file时 使用""并且可以""与使用Microsoft的C / C ++ / .NET编译器 (也接受“ \" 创build的应用程序一起使用。

    • 例如: foo.bat "We had 3"" of rain."

    • 以下仅适用于batch file:

      • ""是获得命令解释器( cmd.exe )将整个双引号string视为单个参数的唯一方法

      • 可悲的是,不仅如此,封闭的双引号(像往常一样)保留下来,而且双引号string也是如此,所以获得预定的string是一个两步的过程。 例如,假定双引号string作为第一个parameter passing, %1

      • set "str=%~1"删除封闭的双引号; set "str=%str:""="%"然后将加倍的双引号转换为单引号。
        请确保在赋值部分周围使用括号双引号,以防止对值进行不必要的解释。

  • \"是许多其他程序 (例如,Perl,Python,Ruby,甚至是微软自己的PowerShell(!)) 所必需的 ,但是它的使用并不安全

    • \"是许多可执行文件和解释器所要求的从外部传递string时,包括微软自己的PowerShell – 或者,在微软编译器的情况下,支持作为 "" 的替代scheme – 最终, 目标程序取决于parsing参数列表。
    • 例如: foo.exe "We had 3\" of rain."
    • 但是,使用\"导致不必要的,执行命令和/或input/输出redirect
      • 以下字符表示此风险: & | < > & | < >
      • 例如,以下结果导致ver命令的意外执行; 请参阅下面的进一步解释和解决方法的下一个要点:
        • foo.exe "3\" of snow" "& ver."
  • 如果你必须使用\" ,那么只有3种安全的方法 ,但是非常麻烦TS的帽子给他的帮助。

    • 在你的batch file中使用(可能是select性的 )延迟variables扩展 ,你可以将文字\"存储在一个variables中,并使用!var!语法将该variables引用到"..."string中 – 请参阅TS的有用答案 。

      • 上述方法虽然繁琐,但有一个好处,就是可以有条不紊地进行应用,并且可以在任何input的情况下稳健地运行
    • 只有使用LITERALstring – 不涉及VARIABLES的string – 你是否得到了一个类似的方法:明确地说: ^ -escape 所有 cmd.exe元字符: " & | < > – 如果你还想禁止variables扩展 – %
      foo.exe ^"3\^" of snow^" ^"^& ver.^"

    • 否则,您必须根据识别stringcmd.exe认为由于错误解释为“ \"而认为没有引用的部分作为closures分隔符来制定您的string

      • 在包含shell元字符的文字部分: ^逃避他们; 使用上面的例子,它必须被^转义:
        foo.exe "3\" of snow" "^& ver."

      • 具有%...%风格的variables引用的部分中 :确保cmd.exe将它们视为"..."string的一部分, 并且variables值本身不具有embedded的不平衡引号 – 这甚至不总是可能的

有关背景信息,请继续阅读。


背景

注意:这是基于我自己的实验。 如果我错了,请告诉我。

像类Unix系统上的类似Bash的shell在将参数单独传递给目标程序之前,对参数列表(string)进行标记化:在其他扩展中,它们将参数列表分成单独的单词(单词分割)和删除引用字符产生的话(引用删除)。 目标程序是从概念上来说是一个单独的参数arrays,删除了(语法要求的)引号。

相比之下,Windows命令解释器显然不标记参数列表,只是传递包含所有参数的单个string(包括引号)。 – 到目标程序。
但是,在将单个string传递给目标程序之前,需要执行一些预处理: ^ escape chars。 除了双引号的string被删除(他们转义下面的字符),并且variables引用(例如, %USERNAME% )首先被内插

因此,与Unix不同的是,目标程序的责任是parsing参数string,并将其拆分成单独的参数,并删除引号。 因此, 不同的程序可以假设需要不同的转义方法并没有一个单一的转义机制, 保证与所有程序一起工作https://stackoverflow.com/a/4094897/45375包含非常好的无政府状态背景是Windows命令行parsing。;

在实践中, \"非常普遍,但不安全 ,如上所述:

由于cmd.exe本身不能识别“ \"作为一个转义的双引号,因此它可能会误解命令行中的后面的标记为未加引号,并可能将其解释为命令和/或input/输出redirect
简而言之,问题曲面(如果有以下任何字符跟随开放或不平衡的 \"& | < > ;例如:

 foo.exe "3\" of snow" "& ver." 

cmd.exe \"作为常规双引号产生以下标记:

  • "3\"
  • of
  • snow" "
  • rest: & ver.

由于cmd.exe认为& ver. 是不加引号的 ,它把它解释为& (命令序列操作符),后面跟着要执行的命令的名称( ver..被忽略; ver报告cmd.exe的版本信息)。
总体效果是:

  • 首先, foo.exe用前3个标记调用foo.exe
  • 然后,执行ver

即使在意外的命令没有伤害的情况下,由于并不是所有的参数都被传递给它,你的总体命令也不会按照devise的那样工作。

许多编译器/解释器只能识别 GNU C / C ++编译器,Python,Perl,Ruby,甚至是从cmd.exe调用的微软自己的PowerShell,对于他们来说,这个问题并没有简单的解决scheme。
从本质上说,你必须事先知道你的命令行的哪些部分被误解为不加引号,并且有select性地回避& | < >所有实例。 在那些部分。

相比之下, 使用""是安全的 ,但遗憾的是只有基于微软编译器的可执行文件和batch file (在batch file的情况下,以上讨论过)支持。

相比之下, 从外部调用的PowerShell脚本(例如,从cmd.exe ,无论是从命令行还是batch file)都只能识别\" ,即使内部 PowerShell使用`作为双引号string中的转义字符,也接受""
同样,传递一个命令string到powershell.exe -c需要\" ;例如,
powershell -c " \"ab c\".length"工作(输出4 ),但是
powershell -c " ""ab c"".length" break。


相关信息

  • ^只能用作未加引号的string中的转义字符 – 在双引号string中, ^不是特殊的,并且被视为文字。

    • CAVEAT在传递给call语句的参数中使用^被打破 (这适用于调用的两个用途:调用另一个batch file或二进制文件,并在同一个batch file中调用一个子例程):
      • 如果variables%v%包含字面值a^b ,则call :foo "%v%""a^^b" (!)赋值给%1 (第一个参数)在子程序中:foo
      • 不加引号使用^call完全中断的 ,因为^不能再用于转义特殊字符 :例如, call foo.cmd a^&b悄悄地中断(而不是像foo.cmd那样传递字面值a&b call ) – foo.cmd永远不会调用(!),至less在Windows 7上。
  • 不幸的是, 转义字面值%是一种特殊情况 ,它需要不同的语法,具体取决于在命令行中是否在batch file中指定了string 。 请参阅https://stackoverflow.com/a/31420292/45375

    • 简而言之:在batch file中,使用%% 。 在命令行上, %不能被转义,但是如果你在一个非引号string(例如, echo %^foo% )的开始,结尾或者variables名里加一个echo %^foo% ,你可以防止variables扩展(插值)。 命令行中不属于variables引用的%实例被视为文字(例如, 100% )。
  • 通常, 要安全地处理可能包含空格和特殊字符的variables值

    • 作业将variables名和值都放在一对双引号中 ; 例如, set "v=a & b"将字面值a & b赋值给variables%v% (相反, set v="a & b"将使双引号部分成为值)。 将% literal实例转义为%% (仅在batch file中工作 – 参见上文)。
    • 参考双引号variables引用以确保它们的值不被插值; 例如, echo "%v%"不会使%v%的值进行插值并打印"a & b" (但是请注意,双引号也总是打印出来)。 相比之下, echo %v%将文字a传递给echo ,将&解释为命令sorting运算符,因此会尝试执行名为b的命令。
      还要注意上面的警告重新使用^ call语句。
    • 外部程序通常会关注去除参数周围的双引号,但是,如上所述,在batch file中,您必须自己做(例如, %~1从第一个参数中删除括起来的双引号),不幸的是, 没有直接的方式,我知道得到echo打印一个variables的价值忠实没有封闭的双引号
      • 只要值没有embedded双引号 , Neil提供了一个基于for based的解决方法 ; 例如:
        set "var=^&')|;,%!" for /f "delims=" %%v in ("%var%") do echo %%~v
  • cmd.exe 不能识别引号作为string分隔符 – 它们被视为文字,通常不能用于分隔embedded的空白string; 也就是说,与单引号相邻的令牌以及其间的任何令牌都被视为未被cmd.exe引用并进行相应的解释。

    • 但是,鉴于目标程序最终执行自己的参数parsing,一些程序(如Ruby)甚至可以在Windows上识别单引号的string; 相比之下,C / C ++可执行文件,Perl和Python 不能识别它们。
      但是,即使目标程序支持,也不build议使用单引号string,因为它们的内容不受cmd.exe可能不需要的解释的保护。

电源shell

Windows PowerShell是比cmd.exe更高级的shell程序,并且多年以来它一直是Windows的一部分。

PowerShell在引用方面始终如一地在内部工作:

  • 在双引号string里面,用`"""来转义双引号
  • 在单引号string中,使用''来转义单引号

这适用于PowerShell命令行,以及在PowerShell中将parameter passing给PowerShell脚本或函数。

(正如上面所讨论的, 从外部向PowerShell传递一个转义的双引号需要\" – 没有其他的工作)。

可悲的是,在调用外部程序时,您需要同时适应PowerShell自身的引用规则 目标程序的转义:

引号string中的引号

考虑"3`" of rain"string"3`" of rain" ,PowerShell内部转换为3" of rain字面3" of rain

如果你想将这个string传递给外部程序, 除了 PowerShell 之外还必须应用目标程序的转义 。 假设你想把string传递给一个C程序,这个C程序需要将embedded的双引号转义为\"

 foo.exe "3\`" of rain" 

请注意, `"使PowerShell高兴” “使目标程序高兴”都是必须存在的。

同样的逻辑适用于调用一个batch file,其中必须使用""

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

相比之下,将引号embedded引号string中根本不需要转义。

引号string内的引号不需要额外的转义; 考虑'2'' of snow' ,这是2' of snow2' of snow PowerShell表示。

 foo.exe '2'' of snow' foo.bat '2'' of snow' 

PowerShell将单引号string转换为双引号string,然后将它们传递给目标程序。

但是, 引号string中的引号 ,不需要为PowerShell转义,对于目标程序仍然需要转义:

 foo.exe '3\" of rain' foo.bat '3"" of rain' 

PowerShell v3引入了magic- --%选项 ,它减轻了一些痛苦,通过将任何东西传递给目标程序之后,除了cmd.exe风格的环境variables引用(例如, %USERNAME% ),它们扩展; 例如:

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

请注意,如何仅将目标程序的embedded式" as \"为(而不是像PowerShell那样)就足够了。

但是,这种方法:

  • 不允许转义%字符以避免环境variables扩展。
  • 排除了直接使用PowerShellvariables和expression式; 相反,命令行必须在第一步中build立在stringvariables中,然后在第二步中用Invoke-Expression

因此,尽pipe有很多进步,但PowerShell在调用外部程序时并没有轻易逃脱。 但是,它引入了对单引号string的支持。

我不知道在Windows世界中是否有可能切换到Unix模式,使shell 不pipe目标程序如何,都可以预先执行所有的标记和引用移除操作,然后通过传递结果标记来调用目标程序。

Google最终拿出了答案。 批处理中stringreplace的语法是这样的:

 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%" 

它将replace" with \"所有实例,妥善转义为bash。

除了mklement0的优秀答案 :

几乎所有的可执行文件都接受\"作为一个转义" 。 但是,在cmd中的安全使用几乎只能使用DELAYEDEXPANSION。
要明确地发送一个文字"到某个进程,把一个环境variables赋值给一个环境variables,然后使用这个variables,只要你需要传递一个引号。 例:

 SETLOCAL ENABLEDELAYEDEXPANSION set q=\" child "malicious argument!q!&whoami" 

注意SETLOCAL ENABLEDELAYEDEXPANSION似乎只能在batch file中使用。 要在交互式会话中获取DELAYEDEXPANSION,请启动cmd /V:ON

如果您的batch file不适用于DELAYEDEXPANSION,则可以暂时启用它:

 ::region without DELAYEDEXPANSION SETLOCAL ENABLEDELAYEDEXPANSION ::region with DELAYEDEXPANSION set q=\" echoarg.exe "ab !q! & echo danger" ENDLOCAL ::region without DELAYEDEXPANSION 

如果要从包含以""forms转义的引号的variables传递dynamic内容,则可以在展开时将""replace为""

 SETLOCAL ENABLEDELAYEDEXPANSION foo.exe "danger & bar=region with !dynamic_content:""=\"! & danger" ENDLOCAL 

这种replace是不安全的%...%风格扩展!

如果是OP bash -c "g++-linux-4.1 !v_params:"=\"!" 是安全的版本。


如果由于某种原因,甚至暂时启用DELAYEDEXPANSION不是一个选项,请继续阅读:

如果一个人总是需要转义特殊字符,而不是仅仅是有时候,那么在cmd中使用\"更安全一些(如果一致,那么忘记插入符号的可能性就会降低)

为了达到这个目的,在引号( ^" )前加引号,应该到达subprocess的引号,因为文字必须另外用反冲( \^" )来转义。 所有的 shell元字符都必须和^一起转义,例如& => ^& ; | => ^| ; > => ^> ; 等等

例:

 child ^"malicious argument\^"^&whoami^" 

来源: 每个人都以错误的方式引用命令行参数 ,请参阅“更好的引用方法”


要传递dynamic内容,需要确保以下内容:
包含该variables的命令部分必须被cmd.exe视为“引用”(如果variables可以包含引号,则不可能这样做 ) – 为了实现这个目的,最后"之前的variables和第一个"variables之后没有^转义。这两个之间的cmd-metacharacters "不能被转义。 例:

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

这是不安全的,如果%dynamic_content%可以包含不匹配的引号。