在批处理脚本中转义双引号
我将如何去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.^"
-
否则,您必须根据识别string
cmd.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上。
- 如果variables
- CAVEAT : 在传递给
-
不幸的是, 转义字面值
%
是一种特殊情况 ,它需要不同的语法,具体取决于在命令行中是否在batch file中指定了string 。 请参阅https://stackoverflow.com/a/31420292/45375- 简而言之:在batch file中,使用
%%
。 在命令行上,%
不能被转义,但是如果你在一个非引号string(例如,echo %^foo%
)的开始,结尾或者variables名里加一个echo %^foo%
,你可以防止variables扩展(插值)。 命令行中不属于variables引用的%
实例被视为文字(例如,100%
)。
- 简而言之:在batch file中,使用
-
通常, 要安全地处理可能包含空格和特殊字符的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
- 只要值没有embedded双引号 , Neil提供了一个基于
- 作业 : 将variables名和值都放在一对双引号中 ; 例如,
-
cmd.exe
不能识别单引号作为string分隔符 – 它们被视为文字,通常不能用于分隔embedded的空白string; 也就是说,与单引号相邻的令牌以及其间的任何令牌都被视为未被cmd.exe
引用并进行相应的解释。- 但是,鉴于目标程序最终执行自己的参数parsing,一些程序(如Ruby)甚至可以在Windows上识别单引号的string; 相比之下,C / C ++可执行文件,Perl和Python 不能识别它们。
但是,即使目标程序支持,也不build议使用单引号string,因为它们的内容不受cmd.exe
可能不需要的解释的保护。
- 但是,鉴于目标程序最终执行自己的参数parsing,一些程序(如Ruby)甚至可以在Windows上识别单引号的string; 相比之下,C / C ++可执行文件,Perl和Python 不能识别它们。
电源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 snow
的2' 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%
可以包含不匹配的引号。