在批处理函数的参数中使用特殊字符的奇怪行为?

Dav*_*vic 6 windows cmd batch-file

假设你跑了 test.bat "blabla,blabla,^>blabla", "blaby"

test.bat实现:

@SETLOCAL
@ECHO OFF
SET list=%~1
ECHO "LIST: %list%"
ECHO "ARG 1: %~1"
ECHO "ARG 2: %~2"
@ENDLOCAL   
@GOTO :EOF
Run Code Online (Sandbox Code Playgroud)

输出符合预期:

"LIST: blabla,blabla,>blabla"
"ARG 1: blabla,blabla,^>blabla"
"ARG 2: blaby"
Run Code Online (Sandbox Code Playgroud)

但是,如果在批处理文件中使test.bat成为一个函数,该怎么办:

@SETLOCAL
CALL :TEST "blabla,blabla,^>blabla", "blaby"
@ENDLOCAL
@GOTO :EOF

:TEST
@SETLOCAL
@ECHO OFF
SET list=%~1
ECHO "LIST: %list%"
ECHO "ARG 1: %~1"
ECHO "ARG 2: %~2"
@ENDLOCAL   
@GOTO :EOF
Run Code Online (Sandbox Code Playgroud)

运行后输出为:

"LIST: blabla,blabla,^"
"ARG 1: blabla,blabla,^^>blabla"
"ARG 2: blaby"
Run Code Online (Sandbox Code Playgroud)

咦?

  1. blabla列表中去了哪里?
  2. ARG 1有^^?为什么?

有人可以解释特殊字符在函数参数中的行为方式与命令行参数不同吗?

dbe*_*ham 9

只需使用以下内容,您就可以使用第一批脚本获得相同的结果:

call test.bat "blabla,blabla,^>blabla", "blaby"
Run Code Online (Sandbox Code Playgroud)

您的问题源于批处理解析CALL语句的一个不幸方面.在Windows 7命令解释器(CMD.EXE)如何解析脚本的第6阶段中进行了描述.

哎哟 - 我以为我理解之前的插入符号加倍,但显然不是.为了回应jeb的评论,我对以下讨论进行了大量编辑.

CMD.EXE的设计者想要一个声明,就像call echo ^^给出相同的结果echo ^^.两个语句都缩减^^^第2阶段,处理特殊字符.但是CALL声明必须第二次经历第1阶段和第2阶段.因此,在幕后,当CMD.EXE识别阶段6中的CALL语句时,它将剩余的插入符号加倍^^,然后第二轮第2阶段将其缩减回^.这两个语句都将单个插入符号回显到屏幕上.

不幸的是,即使引用它们,CMD.EXE也会盲目地将所有插入符号加倍.但引用的插入符号不被视为逃避,它是一个文字.插入符号不再被消耗.非常不幸.

运行call test.bat "blabla,blabla,^>blabla", "blaby"变为call test.bat "blabla,blabla,^^>blabla" "blaby"解析器的第6阶段.

这很容易解释为什么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
Run Code Online (Sandbox Code Playgroud)

以上产生了预期的结果:

"LIST: blabla,blabla,>blabla"
"ARG 1: blabla,blabla,^>blabla"
"ARG 2: blaby"

"LIST: blabla,blabla,>blabla"
"ARG 1: blabla,blabla,^>blabla"
"ARG 2: blaby"
Run Code Online (Sandbox Code Playgroud)

从命令行运行时,扩展规则是不同的.从命令行逃避%是不可能的.相反,您必须在百分比内添加一个插入符号,以防止扩展阶段识别第一遍的名称,然后在阶段2中剥离插入符号时,第二遍扩展正确扩展变量.

以下使用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"
Run Code Online (Sandbox Code Playgroud)

转义的替代方法 - 通过引用传递值!

总而言之,逃避规则非常复杂.这就是高级批处理脚本通常通过引用而不是文字传递字符串值的原因.将所需的字符串放在变量中,然后将变量的名称作为参数传递.延迟扩展用于获取确切的字符串,而不必担心由于特殊字符或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!
Run Code Online (Sandbox Code Playgroud)

这是一个如何工作的演示.

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
Run Code Online (Sandbox Code Playgroud)

  • 它不会回应任何东西,因为在阶段2中,插入符号被消耗掉.所以后来没有任何可以加倍的插入符号,所以只有一个nake&符号,完整的'call echo'失败了.只有在后期扩张的情况下,才能在"呼叫回声"中脱离任何文字. (2认同)