为什么在if语句中无法获取延迟扩展变量的子字符串?

16
为什么在if语句中内联操作字符串可以不使用延迟扩展变量,但在其他情况下会失败。例如:
set test=testString
if %test:~0,4%==test echo Success

这个工作是正确的,返回Success。但是如果我执行以下操作:

setLocal enableDelayedExpansion
set test=testString
if !test:~0,4!==test echo Success

我收到了这个错误信息 - 4!==test was unexpected at this time.

很明显,你可以通过像 set comp=!test:~0,4! 这样的操作来避免这个错误,然后在if语句中使用 !comp! 变量。


3
这段代码的意思是:如果变量 !test 的前四个字符是 "test",那么就输出 Success 。 - npocmaka
2个回答

11

npocmaka准确地诊断了问题,即IF命令具有自己的特殊解析阶段,并且变量扩展中的记号分隔符会导致问题。

替换也会引起问题:

:: This fails
if !test:String=!==test echo Success

普通扩展起作用的原因是因为它在IF解析之前发生,但延迟扩展发生在IF解析之后。

使用转义符号是解决特殊字符问题的替代方法。

:: Both of these work
if !test:~0^,4!==test echo Success
if !test:String^=!==test echo Success

10

,;=<tab><space> 是 cmd.exe 的分隔符,在许多情况下,如果它们不在引号内,则会被忽略并像空格一样起作用。可能在此情况下,, 被视为第一个操作数的结尾,而 IF 感到惊讶,因为没有有效的比较运算符。例如,这将打印出 yep

if a   ;==;,,=a echo yep

(但如果操作数的第一部分有一个等号,它将不起作用)

但这个不行:

if "a ;" == ";,,=a" echo yep

因此,当您使用逗号时,需要用引号来创建有效的IF表达式。这将起作用。

setLocal enableDelayedExpansion
set test=testString
if "!test:~0,4!" == "test" echo Success

如果没有延迟扩展,替换操作将立即执行,并且可以在不加引号的情况下正常工作:

set test=testString
setlocal disableDelayedExpansion
if %test:~0,4% == test echo Succes
endlocal

由于相同的原因,这将被视为语法错误表达式(请参见jeb的评论):

set "test="
setlocal disableDelayedExpansion
if %test% == test echo Succes
endlocal

可能不是完整的答案,但应该很接近。因为echo !test:~0,4!echo %test:~0,4%都可以在不使用引号的情况下工作,所以问题是为什么IF命令会失败 - 可能是因为IF命令使用了自己的解析器

总之 - 当你用IF比较字符串时,最好加上引号:

  1. 使用延迟扩展时,逗号和分号会引起麻烦。
  2. 不使用延迟扩展时,未定义的变量会引起麻烦。

为什么它对%test:~0,4%不起作用? - unclemeat
好的回答 - 我认为你的结论是正确的。虽然,正如你所说,它并没有完全回答问题。如果我没有得到更简明的答案,我会在一段时间内将此答案标记为正确。非常感谢。 - unclemeat
5
关键在于延迟扩展(delayed expansion)就是这个意思——直到执行时间才会进行。在解析时,%var...% 会被解释和替换,但是!var...!不会,它将在运行时被替换,并被解释为一个简单的字符串。因此,解析器看到的是字符串[分隔符]字符串= =字符串,但它期望的是字符串[运算符]字符串。解析器看到的第二个字符串与其有效运算符列表不匹配,因此解析器会将该命令视为无效。 - Magoo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接