批处理 - Set/Reset 参数调用带有感叹号 (!) 和延迟扩展的子程序?

Batch - Set/Reset parameters calling subroutine with exclamation mark (!) and delayed expansion?

我有 2 个 Locals 使用延迟扩展。 1. Local 调用一个包含 2. Local 的子程序,该子程序定义了 2 个变量并使用 FOR /F ... 将这些变量的值设置为参数值。只要字符串不包含一个或多个感叹号,这就可以正常工作。以下工作脚本显示了该问题:

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION

    CALL :get_substrings testString substr
    ECHO Inside 1. local:
    ECHO !testString!
    ECHO !substr!

    EXIT /B 0
ENDLOCAL


:get_substrings
    SETLOCAL ENABLEDELAYEDEXPANSION

    SET string="World^! wasserschutzpolizei^!"
    SET substring="Hello^!"
    ECHO Inside get_substrings:
    ECHO !string!
    ECHO !substring!

    FOR /F "delims=" %%s IN ("!string!") DO FOR /F "delims=" %%b IN ("!substring!") DO (
        ENDLOCAL & SET "%1=%%s" & SET "%2=%%b" & EXIT /B 0
    )
EXIT /B 0

输出:

Inside get_substrings:
"World! wasserschutzpolizei!"
"Hello!"
Inside 1. local:
"World"
"Hello"

预计:

Inside get_substrings:
"World! wasserschutzpolizei!"
"Hello!"
Inside 1. local:
"World! wasserschutzpolizei!"
"Hello!"

如果删除 stringsubstring 中的感叹号,脚本将按预期运行。

因为这只是一个示例,请考虑...

  1. 不使用延迟扩展不是一种选择,而且...
  2. 使用一个 Local 是不可取的。

当然,如果确实需要其中一项或两项,并且没有其他方式,我会接受其他指出这一点的答案。

所以...

  1. 如何获取 1.local 中带有感叹号的值,就像我在 Expected 部分中显示的那样?
  2. 为什么它不适用于当前设置参数值的方式?

感谢您的帮助!

将 :get_substrings 函数的 return 部分更改为:

...
set "return1=!string:"=""!"
set "return1=%return1:!=^^^!%"
set "return1=!return1:""="!"

set "return2=!substring:"=""!"
set "return2=%return2:!=^^^!%"
set "return2=!return2:""="!"

FOR /F "delims=" %%s IN ("!return1!") DO FOR /F "delims=" %%b IN ("!return2!") DO (
    ENDLOCAL & SET "%1=%%s" & SET "%2=%%b" & EXIT /B 0
)

但是为什么你的解决方案失败了?
问题是 set "%1=%%s" 是在延迟扩展上下文中执行的,因为您的整个批处理使用延迟扩展。
但是延迟扩展是批解析器的最后一个扩展阶段,它是在 %%s.
的参数扩展之后 因此解析器会尝试扩展你的感叹号,当这里只有一个时它将被删除,当有两个像 "World! wasserschutzpolizei!" 它扩展变量 ! wasserschutzpolizei! 到它的值(在你的情况下它似乎为空)。

该解决方案只是将插入符号放在感叹号前面。
但是你也在你的样本中使用引号,这让更多的事情变得更复杂。
为避免引号和插入符号出现问题,这是将所有引号加倍、添加插入符号并将双引号改回单引号的简单方法。

是否可以选择在调用子例程时禁用延迟扩展:

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS DISABLEDELAYEDEXPANSION

    CALL :get_substrings testString substr
    SETLOCAL ENABLEDELAYEDEXPANSION
    ECHO Inside 1. local:
    ECHO !testString!
    ECHO !substr!
    ENDLOCAL

    EXIT /B 0
ENDLOCAL


:get_substrings
    SETLOCAL ENABLEDELAYEDEXPANSION

    SET string="World^! wasserschutzpolizei^!"
    SET substring="Hello^!"
    ECHO Inside get_substrings:
    ECHO !string!
    ECHO !substring!

    FOR /F "delims=" %%s IN ("!string!") DO FOR /F "delims=" %%b IN ("!substring!") DO (
        ENDLOCAL & SET "%1=%%s" & SET "%2=%%b" & EXIT /B 0
    )
EXIT /B 0

这将避免在启用延迟扩展时扩展for变量%%s%%b,因为这是最后一步,所以感叹号被消耗了。