通过简单地使用以下内容,您可以获得与第一批脚本相同的结果:
call test.bat "blabla,blabla,^>blabla", "blaby"
你遇到的问题源于批处理如何解析CALL语句的一个不幸特性,这在第6阶段中描述了:《Windows命令解释器(CMD.EXE)如何解析脚本?》。
天啊 - 我以为我之前已经理解过连字符重复了,但显然没有。针对jeb的评论,我进行了大量编辑。
CMD.EXE的设计师希望像call echo ^^
这样的语句与echo ^^
产生相同的结果。两个语句都将^^
在第2阶段缩减为^
,其中处理了特殊字符。但是CALL语句必须再次经过第1和第2阶段。因此,在幕后,当CMD.EXE在第6阶段识别CALL语句时,会将剩余的插入符号加倍到^^
,然后第二轮第2阶段将其缩减回到^
。这两个语句都向屏幕上回显单个插入符号。
不幸的是,CMD.EXE盲目地加倍所有插入符,即使它们被引用也是如此。但是,引用的插入符不会被视为转义符,它是一个字面值。插入符不再被消耗。非常不幸。
在解析器的第6阶段中,运行call test.bat "blabla,blabla,^>blabla", "blaby"
变成了call test.bat "blabla,blabla,^^>blabla" "blaby"
。
这很容易解释为什么你的输出中ARG 1看起来像它的样子。
至于blabla
去哪里了? 这有点棘手。
当你的脚本执行SET list=%~1
时,引号被移除,^^
被视为转义插入符,缩减为^
,而>
则不再被转义。因此,你的SET语句的输出被重定向到一个名为“blabla”的文件中。当然,SET没有输出,所以你的硬盘上应该有一个长度为零的“blabla”文件。
编辑 - 如何使用“延迟扩展”正确传递所需的参数
davor在他的答案中尝试了在调用过程中反转插入符的效果。但这不可靠,因为你无法确定插入符可能被加倍的次数。最好让调用者进行调整来补偿调用。这很棘手 - 你必须使用jeb所称的“延迟扩展”。
在批处理脚本中,你可以定义一个包含所需参数字符串的变量,并延迟扩展直到加倍插入符,方法是用另一个%转义%。对于每个CALL语句,你都需要加倍百分比。
@echo off
setlocal
set arg1="blabla,blabla,^>blabla"
call :TEST %%arg1%% "blaby"
echo(
call call :TEST %%%%arg1%%%% "blaby"
::unquoted test
exit /b
:TEST
setlocal
set list=%~1
echo "LIST: %list%"
echo "ARG 1: %~1"
echo "ARG 2: %~2"
exit /b
以上代码产生了期望的结果:
"LIST: blabla,blabla,>blabla"
"ARG 1: blabla,blabla,^>blabla"
"ARG 2: blaby"
"LIST: blabla,blabla,>blabla"
"ARG 1: blabla,blabla,^>blabla"
"ARG 2: blaby"
当从命令行运行时,扩展规则会有所不同。在命令行中无法转义%。相反,您必须在百分号内添加一个脱字符,以防止扩展阶段在第一遍识别名称,然后在第二阶段去除脱字符时,第二次扩展变量。
以下使用davor的原始TEST.BAT。
C:\test>test.bat "blabla,blabla,^>blabla" "blaby"
"LIST: blabla,blabla,>blabla"
"ARG 1: blabla,blabla,^>blabla"
"ARG 2: blaby"
C:\test>set arg1="blabla,blabla,^>blabla"
C:\test>test.bat %arg1% "blaby"
"LIST: blabla,blabla,>blabla"
"ARG 1: blabla,blabla,^>blabla"
"ARG 2: blaby"
C:\test>call test.bat %^arg1% "blaby"
"LIST: blabla,blabla,>blabla"
"ARG 1: blabla,blabla,^>blabla"
"ARG 2: blaby"
C:\test>set arg2=%^arg1%
C:\test>call call test.bat %^arg2% "blaby"
"LIST: blabla,blabla,>blabla"
"ARG 1: blabla,blabla,^>blabla"
"ARG 2: blaby"
通过引用传递值,而非使用转义符号——一个替代方法!
总的来说,转义规则过于复杂。这就是为什么高级批处理脚本通常会通过引用传递字符串值,而不是作为字面量。期望的字符串被放在一个变量中,然后变量名称被作为参数传递。延迟扩展被用来获取确切的字符串,而不用担心由于特殊字符、CALL字符加倍或百分比剥离而导致的损坏。
这里是一个演示这个概念的简单test.bat文件
@echo off
setlocal enableDelayedExpansion
set "var1=!%~1!"
echo var1=!var1!
call :test var1
exit /b
:test
set "var2=!%~1!"
echo var2=!var2!
这里是演示它如何工作的示例。
C:\test>set complicatedString="This & that ^" ^& the other thing ^^ is 100% difficult to escape
C:\test>set complicatedString
complicatedString="This & that ^" & the other thing ^ is 100% difficult to escape
C:\test>test.bat complicatedString
var1="This & that ^" & the other thing ^ is 100% difficult to escape
var2="This & that ^" & the other thing ^ is 100% difficult to escape
C:\test>call test.bat complicatedString
var1="This & that ^" & the other thing ^ is 100% difficult to escape
var2="This & that ^" & the other thing ^ is 100% difficult to escape
C:\test>call call test.bat complicatedString
var1="This & that ^" & the other thing ^ is 100% difficult to escape
var2="This & that ^" & the other thing ^ is 100% difficult to escape