IF/ELSE 语句中 GOTO 出现意外行为的原因是什么?

What is the reason for the unexpected behavior with GOTO in an IF/ELSE statement?

我写了一个批处理脚本,其中查询在末尾的 IF 语句中出现,它将用户发送回菜单,终止脚本,或者如果输入无效,清除控制台输出并再次提示用户。同样的事情发生在 else 语句的末尾。

如果用户现在做出选择,他总是要做两次。第一次查询只是再次询问。

有人对此有解决方案吗?

部分菜单:

:restart
cls
set /p input=enter your prefered number and press "enter":
if "%input%"=="1" ( goto :installsd 
) else if "%input%"=="4" ( goto :restoreup
) else if "%input%"=="6" ( goto :end 
) else ( goto :restart )

这可以正常工作:

:installsd
...some code...
    :A
    cls
    echo [1] back to menu
    echo [2] exit
    set /p input=enter your prefered number and press "enter":
    if "%input%"=="1" ( goto :restart 
    ) else if "%input%"=="2" ( goto :exit 
    ) else ( goto :A )

但这里我需要双输入才能返回菜单或退出。
在第一次输入后它运行 goto :B/goto :C 然后我回到菜单。

:restoreup
if exist "%FilePath%backup\patch" (
...some code...
    :B
    cls
    echo [1] back to menu
    echo [2] exit
    set /p input=enter your prefered number and press "enter":
    if "%input%"=="1" ( goto :restart 
    ) else if "%input%"=="2" ( goto :exit 
    ) else ( goto :B )  
) else (
...some code...
    :C
    cls
    echo [1] back to menu
    echo [2] exit
    set /p input=enter your prefered number and press "enter":
    if "%input%"=="1" ( goto :restart 
    ) else if "%input%"=="2" ( goto :exit 
    ) else ( goto :C )
    )

我从来都不是以下的忠实粉丝:

if some_condition:
    go_somewhere_else
else:
    do_something

做事方式。在这些情况下 else 是完全不必要的,并且会阻塞本可以更具可读性的代码:

if some_condition:
    go_somewhere_else
do_something

在像 cmd 这样的语言中 尤其如此,它的设计与其说是从废料中拼凑出来,然后用 [=71= 修复了几十年] 解决方案(例如在您的代码中需要延迟扩展复杂语句)。

您的特定问题是由于在执行语句的任何部分之前完整读取和评估语句。所以考虑你的代码段:

if exist "%FilePath%backup\patch" (
    cls
    echo [1] back to menu
    echo [2] exit
    set /p input=enter your prefered number and press "enter":
    if "%input%"=="1"
    :
)

此处的语句是从第一个 if 到终止该 if 语句的 ) 的所有内容。 在其中包含 %input% 评估。

这意味着 %input% 执行语句之前 被评估,因此在 set /p 将其设置为您想要的状态之前。因此它将具有设置为的 previous 值(或者如果没有设置它则为空)。

您可以通过使用延迟扩展来解决此问题,如以下示例脚本所示:

@echo off
setlocal enableextensions enabledelayedexpansion

set var=previous_value
if 1==1 (
    set var=new_value
    echo percent %var%
    echo delayed !var!
)

endlocal

其输出为:

percent previous_value
delayed new_value

因此您可以看到 % 版本是评估 if 语句时的旧值,而 ! 版本是在评估该行时的旧值已执行(不是语句)。


对于复杂的东西,我通常尽量避免 cmd,特别是因为现代 Windows 有 Powershell,我认为这是一种更有用的语言(虽然有点冗长)。

如果您要在 cmd 中编写代码,最好使其尽可能简单,例如 re-doing 您的 if 语句以更简化的形式。例如,您的第一个代码块将是:

:restart
    cls
    set /p input=Enter your preferred number:
    if "%input%"=="1" goto :installsd 
    if "%input%"=="4" goto :restoreup
    if "%input%"=="6" goto :end 
    goto :restart

您需要另一个循环的单独区域将类似于:

:restoreup
    if not exist "%FilePath%backup\patch" goto :ru_nofile
    rem ...some code...
:ru_menu1
    cls
    echo [1] back to main menu
    echo [2] exit
    set /p input=Enter your preferred number:
    if "%input%"=="1" goto :restart 
    if "%input%"=="2" goto :exit 
    goto :ru_menu1

:ru_nofile
    rem ...some code...
:rm_menu2
    cls
    echo [1] back to menu
    echo [2] exit
    set /p input=Enter your preferred number:
    if "%input%"=="1" goto :restart 
    if "%input%"=="2" goto :exit 
    goto :ru_menu2

而且,是的,它看起来更像是汇编程序(或非常古老的 BASIC)代码,但它至少确保行和语句之间存在 one-to-one 对应关系,这意味着您不会被 non-delayed 展开.