IF为什么不能正确分支?

时间:2019-11-12 02:37:36

标签: cmd

我已经在SS64中问过这个问题。

我正在尝试使用IF DEFINED来控制分支,但是它不起作用。我将从一个例子开始

setLocal
if defined _var (
::  _var is NOT defined, so the next line should not be executed
    if %_var%=="test" echo %_var% is defined
)
endLocal

处理程序在遇到ECHO语句时停止,并以“此时回声意外”退出。错误信息。 问题在于嵌套的IF语句正在执行,并且不应是因为未定义_var。我该如何解决?

谢谢, 谢恩。

2 个答案:

答案 0 :(得分:1)

真正的问题是您没有正确匹配if语句。要进行解释,请参见以下示例。

if a == "a"..

vs

if "a"=="a"..

因此在您的示例中,您尝试匹配:

if test=="test"..

我们在变量的两边使用双引号的原因是为了确保我们消除可能成为测试值一部分的任何空白。

所以这个版本应该没问题。我添加了/i开关以匹配不区分大小写的test,因此它在任何情况下都将匹配testTesTtEstTESTtest等:

setLocal
set /p "_var=Enter Variable name: "
set "_var=%_var:"=%"
if defined _var (
:# rem _var is NOT defined, so the next line should not be executed
    if /i "%_var%"=="test" echo %_var% is defined
)
endLocal

答案 1 :(得分:1)

这个小代码块有多个问题。

第一个问题是使用带有::的无效标签在以(开头并以)结束匹配的命令块内写注释。命令块内不允许使用标签。 0或更多空格/制表符之后以:开头的行导致执行命令块时出现不确定的行为。未定义的行为意味着它可以偶然工作,但也可能偶然失败。执行行为是不确定的。

用于评论的命令 REM 。在命令提示符窗口rem /?中运行以获取有关此命令的帮助。

请注意, REM 命令,因此,与其他命令一样,Windows命令处理器也将解析带有 REM 的注释行。在注释行上很重要的行包含%variable%或类似&|<>的运算符。因此,请注意注释文本中的内容。


第二个问题是评论本身不正确。

  

_var未定义,因此不应执行下一行

此注释是错误的,因为肯定在执行命令_var并获得肯定结果后到达此行的cmd.exe上定义了环境变量if defined _var


第三个问题是,if %_var%=="test"在语法上不是100%正确的。比较运算符==周围缺少空格。但是,cmd.exe会通过在运算符==周围插入两个缺少的空格来自动更正批处理文件时自动检测到此语法错误。可以在debugging the batch file上看到。


第四个问题是 IF 总是比较两个字符串,并在字符串比较中包括双引号。因此,在使用if %_var%=="test"或语法正确的if %var% == "test"时,条件只有在分配给环境变量_var的字符串"test"中带有双引号的情况下才成立。在之前定义的_var的所有其他情况下,如果没有导致语法错误,则此字符串比较的结果为false,请参见下一个问题。

请阅读我在Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files上的答案,其中非常详细地说明了如何通过命令 IF 完成字符串比较。


第五个问题是Windows命令处理器在执行命令 IF 之前,解析从(开始并以匹配的)结尾的整个命令块。

没有环境变量_var的内部 IF 定义是在以下行中解析命令块的结果:

if =="test" echo is defined

cmd.exe在此命令行上检测到严重的语法错误,并在执行外部 IF 条件之前,由于此语法错误而退出批处理文件处理。

一种解决方案是将%var%用双引号引起来,即在批处理文件中使用:

if "%_var%" == "test" echo %_var% is defined

如果在解析命令块时未定义环境变量_var,则此 IF 命令行正确。

但是,如果定义了_var并包含一个或多个",则此解决方案将不起作用,因为双引号再次导致使用命令 IF 。

如果分配给环境变量的字符串包含以下字符之一,则进一步echo不会打印环境变量_var的值。好吧,由于字符串比较结果为false,因此在这种情况下不会执行&|<>

因此,更好的解决方案是启用delayed expansion并使用延迟的环境变量扩展引用环境变量echo的值,以避免该环境变量的值修改命令行以{{1 }}解析整个命令块之后。

_var

还必须启用命令扩展名,因为否则命令 IF 不支持cmd.exe。默认情况下,命令扩展名是启用的,但是最好编写一个不依赖于批处理文件外部定义的设置或默认值的批处理文件。

好吧,setlocal EnableExtensions EnableDelayedExpansion if defined _var ( rem _var is defined, so the next line should be executed. if "!_var!" == "test" echo _var is defined with !_var!. ) endlocal 命令也可以写成:

if defined

在使用echo时,echo _var is defined with test. 不可能在这里打印echo以外的内容。

另一种解决方案是避免使用命令块,并确保分配给环境变量test的字符串值不包含任何!_var!并输出此环境变量的值并以双精度括起来用引号避免_var影响"命令行的执行。

&|<>

此处的字符串比较不区分大小写。

另请参阅:

必须始终确保用户输入的字符串或作为参数传递给批处理文件的字符串或由批处理文件本身确定的字符串(例如,从文件/文件夹名称/路径确定)用双引号引起来,并且本身不包含双引号,以便批处理文件正确处理。虽然文件/文件夹名称中不允许使用字符echo,但文件/文件夹名称中允许使用setlocal EnableExtensions DisableDelayedExpansion if not defined _var goto Label rem _var is defined, so the next line should be executed. rem Remove all double quotes from string value of environment variable. rem It is important to use double quotes around argument string of the rem command SET as otherwise a string with "&|<>" would be not correct rem assigned to the environment variable. set "_var=%_var:"=%" rem It could happen that _var is not defined anymore. But in this case rem it is no problem anymore that cmd.exe replaces all %var% by nothing rem on entire command line before executing the IF condition as the rem remaining commands on this command line with enclosing the string rem value of _var in double quotes is always syntactically correct even rem on string value containing one or more of the operators "&|<>". if /I "%_var%" == "test" echo _var is defined with "%_var%". :Label endlocal ,导致很多批处理文件被解释为文件/文件夹字符串中的AND运算符用双引号引起来,即使只是|<>只需使用文件/文件夹名称即可。

另请参阅有关Naming Files, Paths, and Namespaces的Microsoft文章。


理解批处理文件How does the Windows Command Interpreter (CMD.EXE) parse scripts?上的理解非常重要,必须始终考虑到命令行最终在执行时会如何看待在命令行或整个命令的解析阶段扩展了环境变量引用的情况块。解析后最终执行的命令行必须使用有效的语法,否则&退出批处理文件处理。