7

假设你跑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

输出如预期:

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

但是如果你让 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

运行后输出为:

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

嗯?

  1. blablaLIST 去了哪里?
  2. ARG 1 有^^? 为什么?

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

4

2 回答 2

10

只需使用以下命令,您就可以使用第一个批处理脚本获得相同的结果:

call test.bat "blabla,blabla,^>blabla", "blaby"

您的问题源于批处理如何解析 CALL 语句的一个不幸方面。它在Windows 命令解释器 (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

以上产生了预期的结果:

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

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

从命令行运行时扩展规则不同。从命令行转义 % 是不可能的。相反,您必须在百分比内添加一个插入符号,以防止扩展阶段在第 1 遍识别名称,然后当插入符号在第 2 阶段被剥离时,第 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"

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

总而言之,逃生规则复杂得离谱。这就是为什么高级批处理脚本通常通过引用而不是文字来传递字符串值。所需的字符串被放置在一个变量中,然后变量的名称作为参数传递。延迟扩展用于获取准确的字符串,而不必担心由于特殊字符或 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
于 2012-09-29T21:50:36.737 回答
0

经过一些测试和 dbenham 的回答后,似乎需要预测双插入符号并将其替换为单插入符号:

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

:TEST
@SETLOCAL
@ECHO OFF
SET "list=%~1" & REM CHANGED
SET "list=%list:^^=^%" & REM ADDED
ECHO "LIST: %list%"
ECHO "ARG 1: %~1"
ECHO "ARG 2: %~2"
@ENDLOCAL   
@GOTO :EOF

那么输出是:

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

还要注意奇怪的事情:在SET "list=%list:^^=^%"between^^%%视为两个字符,而不是作为转义的^.

于 2012-09-30T14:39:34.463 回答