如何使用转义双引号替换批处理文件参数中的所有双引号?这是我当前的批处理文件,它扩展了字符串中的所有命令行参数:
@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(这非常失败.)
我想如果我可以将参数连接成一个单独的字符串然后转义它应该正常工作的引号,但我很难确定如何做到这一点.有人知道吗?
批处理脚本中的转义字符是^
.但对于双引号字符串,请将引号加倍:
"string with an embedded "" character"
eplawless自己的答案简单而有效地解决了他的具体问题:它取代"
了整个参数列表中的所有实例\"
,这就是Bash在双引号字符串中需要双引号来表示的方式.
通常使用cmd.exe
Windows命令行解释器(无论是在命令行上 - 通常仍被错误地称为"DOS提示符")还是在批处理文件中回答如何在双引号字符串中转义双引号的问题:请参阅底部以了解PowerShell.
tl;博士:
您必须使用""
合格的字符串时,以(另)一个批处理文件,您可以使用""
与创建应用微软的C/C++/NET编译器.(它也接受\"
),它在Windows 包括Python和Node.js的:
例: foo.bat "We had 3"" of rain."
以下内容仅适用于批处理文件:
""
是获取命令解释器(cmd.exe
)将整个双引号字符串视为单个参数的唯一方法.
然而,遗憾的是,不仅保留了封闭的双引号(像往常一样),但是双重转义的双引号也是如此,因此获得预期的字符串是一个两步过程; 例如,假设双引号字符串作为第一个参数传递,%1
:
set "str=%~1"
删除封闭的双引号; set "str=%str:""="%"
然后将doubled双引号转换为单引号.
请务必在赋值部分周围使用括号双引号,以防止对值进行不必要的解释.
\"
是许多其他程序所必需的 - 作为唯一的选择 -(例如,Ruby,Perl,甚至微软自己的PowerShell(!)),但它的使用并不安全:
\"
是许多可执行文件和解释器要求的 - 包括Microsoft 从外部传递字符串时自己的PowerShell - 或者,在Microsoft的编译器 ""
的情况下,支持作为替代 - 最终,最终由目标程序来解析参数列表.
例: foo.exe "We had 3\" of rain."
然而,使用可\"
导致不经授权,任意执行命令和/或输入/输出重定向:
以下字符存在此风险: & | < >
例如,以下结果导致意外执行ver
命令; 请参阅下面的解释和解决方法的下一个要点:
foo.exe "3\" of snow" "& ver."
对于仅适用于Windows的PowerShell,它是一种强大的替代方案.\""
如果你必须使用"^""
,那么只有3种安全的方法,但是非常麻烦:给TS提供帮助.
在批处理文件中使用(可能是选择性的)延迟变量扩展,您可以将文字存储\"
在变量中,并\"
使用"..."
语法在字符串中引用该变量 - 请参阅TS的有用答案.
上述方法虽然繁琐,但具有以下优点:您可以有条不紊地应用它,并且可以通过任何输入稳健地工作.
只有LITERAL字符串 - 不涉及VARIABLES - 你会得到一个类似的有条理的方法:!var!
断言 - 所有 ^
元字符: cmd.exe
- 如果你也想抑制变量扩展 - " & | < >
:
%
否则,您必须基于识别该字符串的部分制定您的字符串foo.exe ^"3\^" of snow^" ^"^& ver.^"
认为加引号是由于曲解cmd.exe
为结束定界符:
在包含shell元字符的文字部分中: \"
-escape them; 使用上面的例子,它^
必须是&
-escaped:
^
在带有foo.exe "3\" of snow" "^& ver."
-style变量引用的部分中:确保将%...%
它们视为cmd.exe
字符串的一部分,并确保变量值本身不具有嵌入的,不平衡的引号 - 这甚至不可能.
有关背景信息,请继续阅读.
注意:这是基于我自己的实验.如果我错了,请告诉我.
类似POSIX的shell(如类Unix系统上的Bash)在将参数单独传递给目标程序之前将参数列表(字符串)标记化:在其他扩展中,它们将参数列表拆分为单个单词(单词拆分)并从中删除引用字符结果词(引用删除).目标程序的概念是概念上删除了(语法要求)引号的单个参数数组.
相比之下,Windows命令解释器显然没有对参数列表进行标记,只是传递包含所有参数的单个字符串- 包括引用字符. - 到目标计划.
但是,在将单个字符串传递给目标程序之前会进行一些预处理:"..."
escape chars.删除双引号字符串之外的内容(它们将转义以下字符.),并首先插入变量引用(例如^
).
因此,与Unix不同,目标程序负责解析参数字符串并将其拆分为单个参数并删除引号.因此,不同的程序可以假设需要不同的转义方法,并且没有单一的转义机制可以保证与所有程序一起使用 - /sf/ask/17360801/包含有关Windows命令行无政府状态的优秀背景解析.
在实践中,%USERNAME%
如上所述,非常常见但不安全:
由于\"
它本身不会被识别cmd.exe
为转义双引号,因此它可能会在命令行上将后来的令牌误解为不带引号,并可能将它们解释为命令和/或输入/输出重定向.
概括地说:这个问题的表面,如果以下任意字符的遵循开口或不平衡 \"
:\"
; 例如:
foo.exe "3\" of snow" "& ver."
& | < >
看到以下令牌,因为误解cmd.exe
为常规双引号:
\"
"3\"
of
休息: snow" "
由于& ver.
认为cmd.exe
是不带引号的,它将其解释为& ver.
(命令排序运算符),然后是要执行的命令的名称(&
- ver.
被忽略; .
报告ver
的版本信息).
总体效果是:
首先,cmd.exe
仅使用前3个令牌调用.
然后,foo.exe
执行命令.
即使在意外命令没有损害的情况下,由于并非所有参数都传递给它,因此您的整体命令将无法按设计工作.
许多编译器/解释器只能识别ver
- 例如,GNU C/C++编译器,Python,Perl,Ruby,甚至微软自己的PowerShell \"
- 从PowerShell中调用- cmd.exe
对于它们来说,没有简单的解决方案来解决这个问题.
从本质上讲,您必须事先知道命令行的哪些部分被误解为未引用,并且有选择地\""
- ^
在这些部分中覆盖所有实例.
相比之下,使用& | < >
SAFE是令人遗憾的,但令人遗憾的是,只有基于Microsoft编译器的可执行文件和批处理文件才能支持(在批处理文件的情况下,上面讨论了这个问题).
相比之下,PowerShell 从外部调用时- 例如,""
从命令行或批处理文件调用- 仅识别,并且在Windows上,更强大 cmd.exe
cmd.exe
,即使内部 PowerShell \"
用作双引号字符串中的转义字符并接受"""
; 例如:
\""
工作(输出"^""
),更强大
`
,
但是""
打破了.
cmd.exe
只能用作不带引号的字符串中的转义字符 - 在双引号字符串中,""
不是特殊的并且被视为文字.
CAVEAT:传递给语句的powershell -c " ""ab c"".length "
in参数的使用\"
被破坏(这适用于以下两种用途"""
:调用另一个批处理文件或二进制文件,并在同一批处理文件中调用子例程):
powershell -c " \"ab c\".length "
双引号值中的实例莫名其妙地加倍,改变了传递的值:例如,如果变量5
包含文字值cmd.exe
,则在子例程powershell -c " \"a& c\".length "
中将&
(!)分配给^&
(第一个参数)\""
.
未加引号的使用powershell -c " \""a& c\"".length "
与4
被完全损坏在"^""
不能再用于转义特殊字符:例如,\""
悄悄地打破(而不是通过文字powershell -c " "^""a& c"^"".length "
太多&
,因为将没有的情况下5
) -pwsh -c " "^""a& c"^"".length "
是从来没有被调用,至少在Windows(!) 7.
逸出一个文字4
是一种特殊情况下,不幸的是,这需要根据是否在指定的字符串不同句法的命令行与一个批处理文件内 ; 请参阅/sf/ask/17360801/
缺点:在批处理文件中,使用\""
.在命令行上,pwsh
无法转义,但如果bash
在开头,结尾或在未加引号的字符串中放置变量名称(例如\"
),则可以防止变量扩展(插值); ^
命令行上不属于变量引用的实例被视为文字(例如^
).
通常,要安全地处理可能包含空格和特殊字符的变量值:
分配:附上两个变量的名称和在该值单一对双引号的 ; 例如,^
将字面值赋给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
忠实地打印一个变量值而没有封闭的双引号.
Neil提供了一个%
基于变量的解决方法,只要该值没有嵌入双引号就可以工作 ; 例如:
%%
%
并不能识别单个 -quotes作为字符串分隔符-它们被视为文字和一般不能用于分隔带有嵌入空白字符串; 此外,紧接着单引号和其间的任何代币的代币被视为未引用^
并相应地解释.
但是,鉴于目标程序最终执行自己的参数解析,某些程序(如Ruby)甚至可以在Windows上识别单引号字符串; 相比之下,C/C++可执行文件,Perl和Python 无法识别它们.
但是,即使目标程序支持,也不建议使用单引号字符串,因为它们的内容不受保护,不受可能不需要的解释的影响echo %^foo%
.
Windows PowerShell是一个比%
它更高级的shell ,多年来它一直是Windows的一部分(PowerShell Core也为macOS和Linux带来了PowerShell体验).
PowerShell 在引用方面始终在内部工作:
在双引号字符串内,使用100%
或set "v=a & b"
转义双引号
在单引号字符串中,用于a & b
转义单引号
这个工程的PowerShell命令行和参数传递到PowerShell脚本或函数时内的PowerShell.
(如上所述,从外部将转义的双引号传递给PowerShell 需要%v%
或更强大地set v="a & b"
- 没有其他工作原理).
遗憾的是,在调用外部程序时,您需要同时适应PowerShell自己的引用规则并为目标程序转义:
此问题行为也在此GitHub文档问题中进行了讨论和总结
双内-quotes 双 -quoted字符串:
考虑字符串%
,PowerShell内部转换为文字%%
.
如果要将此字符串传递给外部程序,除了 PowerShell 之外,还必须应用目标程序的转义 ; 假设您想将字符串传递给C程序,该程序希望嵌入的双引号可以转义为echo "%v%"
:
$ pwsh -c " \"a& c|\".length" # OK: 5
注意如何既 %v%
-使PowerShell的幸福- 和的"a & b"
-使目标程序高兴-必须存在.
相同的逻辑适用于调用批处理文件,echo %v%
必须使用以下内容:
foo.exe "3\`" of rain"
相比之下,将单引号嵌入双引号字符串中根本不需要转义.
单内-quotes 单 -quoted字符串就不会需要额外的转义; 考虑一下a
,这是PowerShell的代表echo
.
foo.bat "3`"`" of rain"
PowerShell将单引号字符串转换为双引号字符串,然后再将它们传递给目标程序.
但是,单引号字符串中的双引号(不需要为PowerShell转义)仍然需要为目标程序进行转义:
foo.exe '2'' of snow' foo.bat '2'' of snow'
的PowerShell v3的推出了魔法&
选项,叫做停止解析符号,这减轻一些痛苦,通过传递东西后,不解释到目标程序,保存b
风格的环境变量引用(例如,^
),这是扩大; 例如:
foo.exe '3\" of rain' foo.bat '3"" of rain'
注意如何逃避嵌入call
作为%~1
目标程序只(不也为PowerShell的作为echo
)就足够了.
但是,这种方法:
不允许转义 for
字符以避免环境变量扩展.
排除直接使用PowerShell变量和表达式; 相反,命令行必须在第一步中构建在字符串变量中,然后set "var=^&')|;,%!"
for /f "delims=" %%v in ("%var%") do echo %%~v
在一秒内调用.
因此,尽管有许多进步,但PowerShell在调用外部程序时并没有更容易逃脱.但是,它引入了对单引号字符串的支持.
我不知道,如果它基本上可以在Windows世界永远切换到让的Unix模式的外壳做的所有标记化和引用的去除可以预见,在前面,无论目标程序的通过,使得到的令牌,然后调用目标程序.
谷歌最终想出了答案.批处理中字符串替换的语法是:
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%"
它取代的所有实例"
有\"
,正确转义为庆典.
作为mklement0的优秀答案的补充:
几乎所有可执行文件都接受\"
转义"
.但是,使用DELAYEDEXPANSION几乎只能在cmd中安全使用.
要明确地将文字发送"
到某个进程,分配\"
给环境变量,然后在需要传递引用时使用该变量.例:
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中使用会更安全一些.(如果它一致,它不太可能忘记插入符号......)
为了达到这个目的,可以在任何带有插入符号(^"
)的引号之前,引号应该到达子进程,因为文字必须另外使用反斜杠(\^"
)进行转义.所有 shell元字符也必须进行转义^
,例如&
=> ^&
; |
=> ^|
; >
=> ^>
; 等等
例:
child ^"malicious argument\^"^&whoami^"
来源:每个人都以错误的方式引用命令行参数,请参阅"更好的引用方法"
要传递动态内容,需要确保以下内容:
包含变量的命令部分必须被视为"引用" cmd.exe
(如果变量可以包含引号,则不可能这样做%var:""=\"%
- 不要写).为了实现这一点,"
变量之前的最后一个变量和"
变量之后的第一个不是^
-escaped.这两者之间的cmd-metacharacters "
不得转义.例:
foo.exe ^"danger ^& bar=\"region with %dynamic_content% & danger\"^"
如果%dynamic_content%
可以包含无与伦比的引号,这是不安全的.