批处理 - 延迟扩展不适用于周围的 if 子句

Batch - Delayed expansion doesn't work with surrounding if clause

在下面的脚本中,我调用了一个带有字符串的子例程,该字符串的子字符串的最大长度和第三个不存在的变量来取回子字符串。

该脚本应检查具有最大子字符串长度的字符串之后的下一个字符是否为 space,如果是,则按 space 剪切字符串(删除 space to) 并返回子字符串,并通过剪切子字符串部分来更改传递的字符串。示例:

字符串:"Hello World Bla"
子串长度:5

CALL :get_substring string substringLength substring

=> 字符串:"World Bla"(无 space),子字符串长度:5(无变化),子字符串:"Hello"

这在没有 if 子句的情况下工作正常,但当我使用 if 子句时它不起作用,即使我使用延迟扩展也是如此。

这是没有 if 语句的工作代码:

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
    SET string=Hello World wasserschutzpolizei
    SET /A substringLength=5
    CALL :get_substring string substringLength substring
    ECHO !string!
    ECHO !substring!
    EXIT /B 0
ENDLOCAL

:get_substring
    SETLOCAL ENABLEDELAYEDEXPANSION

    SET "string=!%~1!"
    SET "substringLength=!%2!"
    SET nextChar=!string:~%substringLength%,1!

    REM IF "!nextChar!"==" " (
        SET substring=!string:~0,%substringLength%!
        ECHO !substring!
        SET /A cutSpaceCount=!substringLength!+1
        SET string=!string:~%cutSpaceCount%!

        ECHO !string!

        ENDLOCAL & SET %1=%string% & SET %3=%substring% & EXIT /B 0
    REM ) ELSE (
    REM     Some other case    
    REM )

EXIT /B 0

当我在 if 语句中注释时,这不起作用:

IF "!nextChar!"==" " (
    SET substring=!string:~0,%substringLength%!
    ECHO !substring!
    SET /A cutSpaceCount=!substringLength!+1
    SET string=!string:~%cutSpaceCount%!

    ECHO !string!

    ENDLOCAL & SET %1=%string% & SET %3=%substring% & EXIT /B 0
) ELSE (
    REM Some other case    
)
  1. 为什么脚本不能使用 if 语句?
  2. 如何解决?

请注意,我的例程还应该包含一个 else 语句,由于问题相同,我将其删除。

问题出在这一行:

SET string=!string:~%cutSpaceCount%!

当此行放在 IF 命令内时,cutSpaceCount 的值会更改 的代码块(括号)内 IF,因此必须通过 !cutSpaceCount! 延迟扩展来扩展, 而不是 通过 %cutSpaceCount% 标准扩展。

你应该使用类似 "double delayed expansion" 的东西,也就是说,类似于这个结构:

SET string=!string:~!cutSpaceCount!!

当然,这行不通,所以诀窍是使用for命令获取first延迟扩展的值,然后使用FOR参数完成第二次延时扩容:

for /F %%c in ("!cutSpaceCount!") do SET "string=!string:~%%c!"

当子程序中的最终值返回给调用程序时,会发生类似的问题。这是最终的工作代码:

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
    SET string=Hello World wasserschutzpolizei
    SET /A substringLength=5
    CALL :get_substring string substringLength substring
    ECHO !string!
    ECHO !substring!
    EXIT /B 0
ENDLOCAL

:get_substring
    SETLOCAL ENABLEDELAYEDEXPANSION

    SET "string=!%~1!"
    SET "substringLength=!%2!"
    SET "nextChar=!string:~%substringLength%,1!"

    IF "!nextChar!"==" " (
        SET "substring=!string:~0,%substringLength%!"
        ECHO !substring!
        SET /A cutSpaceCount=!substringLength!+1

        for /F %%c in ("!cutSpaceCount!") do SET "string=!string:~%%c!"

        ECHO !string!
        for /F "delims=" %%s in ("!string!") do for /F "delims=" %%b in ("!substring!") do (
            ENDLOCAL & SET "%1=%%s" & SET "%3=%%b" & EXIT /B 0
        )

    ) ELSE (
        ECHO Some other case    
    )

PS - 您不需要在 SET /A 命令中扩展变量值。而不是:

SET /A cutSpaceCount=!substringLength!+1

您可以简单地使用:

SET /A cutSpaceCount=substringLength+1

您似乎对调用延迟扩展时发生的操作顺序很困惑。

首先,var的值被替换为%var%

然后!var!使用结果进行评估。

这一系列操作的范围是一个逻辑行,可能是一个物理[=80] =] 行或任意数量的物理行以终端 ^ 或更常见的使用带括号的行序列继续。

那么在你的主线中,

CALL :get_substring string substringLength substring
ECHO !string!
ECHO !substring!
ENDLOCAL & SET %1=%string% & SET %3=%substring% & EXIT /B 0

由于这些语句在同一逻辑行中,它们将单独评估,所以 !var!==%var%.

在您的子例程(非 IF 版本)中,

    SET substring=!string:~0,%substringLength%!
    ECHO !substring!
    SET /A cutSpaceCount=!substringLength!+1
    SET string=!string:~%cutSpaceCount%!

    ECHO !string!

又是个人陈述。第一个set会先代入substringlength,然后执行SET substring=!string:~0,5!作为第二个操作。

每个 echo 都是一个独立的语句,! 可以(最好应该)替换为 %

set /a 语句 - 好吧,set /a 允许使用未修饰的变量的 当前 值,因此 SET /A cutSpaceCount=substringLength+1SET /A cutSpaceCount=%substringLength%+1可以在这里使用,没有任何逻辑效果。

ENDLOCAL & SET %1=%string% & SET %3=%substring% & EXIT /B 0 将根据先前代码序列建立的值进行评估。

但是 当您添加 if 时,代码被括起来,因此成为一个逻辑语句并且行为不同。

echoes 然后 需要 ! 因为你想在 modified 中显示值=67=]代码块。由于 cutSpaceCount 未在代码块的开头设置,因此 SET string=!string:~%cutSpaceCount%! 将被评估为 SET string=!string:~!

然后 ENDLOCAL & SET %1=%string% & SET %3=%substring% & EXIT /B 0 将适当地替换变量的值 当遇到 IF[=80] =]

因此,替换例程可能是

:get_substring
SETLOCAL ENABLEDELAYEDEXPANSION

SET "string=!%~1!"
SET "substringLength=!%2!"
SET "substring=!string:~0,%substringLength%!"
SET "string=!string:~%substringLength%!"
IF "%string:~0,1%"==" " SET "string=%string:~1%"
ENDLOCAL & SET "%1=%string%" & SET "%3=%substring%"
EXIT /B 0

正如其他人已经解释的那样,问题在于:

SET string=!string:~%cutSpaceCount%!

因为您对变量 cutSpaceCount 使用了直接扩展 (%),它在代码的相同逻辑 line/block 中发生了变化。

一个可能的解决方案是像这样使用 call

    call set "string=%%string:~!cutSpaceCount!%%"

call引入了另一个变量扩展阶段,所以序列如下:

  1. 立即扩展阶段,其中 %% 变为 %:

    call set "string=%string:~!cutSpaceCount!%"
    
  2. 然后发生延迟扩展(假设样本值为5):

    call set "string=%string:~5%"
    
  3. call引入的另一个直接扩展阶段,最终得到%string:~5%