带有用户输入的 Windows 批处理 if 语句

Ced*_*ric 3 windows cmd if-statement batch-file

.bat 应该将用户输入与值进行比较。IF "%choice%"=="1"当我用 1 或 2 的输入测试它时,它不会进入。

这是批处理文件:

@echo off
rem ...
IF [%$ecbId%] == [] (
    echo Enter '1' to blabla
    echo Enter '2' to blabla
    echo Enter anything to abort
    set /p choice="Type input: "
    IF "%choice%"=="1" (
        echo toto1
        rem save into property file
        echo currentMaster=%ecb%>> MDC.properties
        echo masterDcName=ECBDC>> MDC.properties
        echo currentClone=%bdf%>> MDC.properties
        echo cloneDcName=BDFDC>> MDC.properties
    )
    IF "%choice%"=="2" (
        echo toto2
        rem save into property file
        echo currentMaster=%bdf%>> MDC.properties
        echo masterDcName=BDFDC>> MDC.properties
        echo currentClone=%ecb%>> MDC.properties
        echo cloneDcName=ECBDC>> MDC.properties
    )
    IF NOT "%choice%"=="1" (
        echo toto3
        IF NOT "%choice%"=="2" (
            echo toto4
            echo Unknown input ... Aborting script
            exit /b 400
        )
    )
)
Run Code Online (Sandbox Code Playgroud)

命令输出:

Enter '1' to blabla
Enter '2' to blabla
Enter anything to abort
Type input: 1
toto3
toto4
Unknown input ... Aborting script
Run Code Online (Sandbox Code Playgroud)

为什么它不进入IF "%choice%"=="1"条件?

Mof*_*ofi 9

这是一个工作批处理代码:

@echo off
setlocal EnableExtensions EnableDelayedExpansion
rem ...
if "%$ecbId%" == "" (
    echo Enter '1' to blabla
    echo Enter '2' to blabla
    echo Enter anything else to abort.
    echo/
    set "UserChoice=abort"
    set /P "UserChoice=Type input: "
    if "!UserChoice!" == "1" (
        echo toto1
        rem save into property file
        >>MDC.properties echo currentMaster=%ecb%
        >>MDC.properties echo masterDcName=ECBDC
        >>MDC.properties echo currentClone=%bdf%
        >>MDC.properties echo cloneDcName=BDFDC
    )
    if "!UserChoice!" == "2" (
        echo toto2
        rem save into property file
        >>MDC.properties echo currentMaster=%bdf%
        >>MDC.properties echo masterDcName=BDFDC
        >>MDC.properties echo currentClone=%ecb%
        >>MDC.propertiesecho cloneDcName=ECBDC
    )
    if not "!UserChoice!" == "1" (
        echo toto3
        if not "!UserChoice!" == "2" (
            echo toto4
            echo Unknown input ... Aborting script
            endlocal
            exit /B 400
        )
    )
)
endlocal
Run Code Online (Sandbox Code Playgroud)

使用命令在批处理文件的顶部启用延迟扩展setlocal EnableExtensions EnableDelayedExpansion。命令SETLOCAL不仅启用延迟扩展,还将整个命令进程环境保存在堆栈中,如我在更改目录命令 cd ..not working in batch file after npm install 的回答中详细解释的那样。命令进程环境在执行命令ENDLOCAL 时从堆栈中恢复,这也将由 Windows 命令处理器在未明确执行时退出批处理文件处理时完成endlocal

开始与在命令块设置或修改的所有环境变量(,并与匹配的结束)和相同命令块中还引用必须使用延迟扩展被引用。

当 Windows 命令处理器到达第四行时,直到最后一行的整个命令块都被解析和预处理,然后再评估IF条件。在这个预处理步骤中,从第 4 行到第 36 行的整个块中扩展了所有用百分号引用的环境变量。

这意味着所有出现的%ecb%%bdf%都被环境变量的当前值替换,ecb并且bdf在第4 行上的IF条件完全像%$ecbId%IF条件本身中一样被评估之前。

只是UserChoice在解析/预处理整个块时不会扩展环境变量,因为此环境变量是使用延迟扩展引用的。

因此,可以为abort环境变量分配一个默认值,例如UserChoice在批处理用户点击的情况下只输入RETURNENTER不输入任何内容。

由于对环境变量使用延迟扩展,用户输入的字符串永远不会在解析/预处理阶段扩展UserChoice。的当前字符串值UserChoice始终在评估每个IF条件时进行解释。

Windows 命令处理器的这种行为可以很容易地观察到

  1. @echo off从批处理文件的第一行中删除,
  2. 打开命令提示符窗口,
  3. 如果当前目录不是批处理文件的目录,则通过输入带有完整路径的批处理文件的名称在命令提示符窗口中运行批处理文件。

Windows 命令处理器现在总是在解析后输出行或命令块,然后才真正执行。因此可以看出,在达到第一个IF条件时,%...%如果参考环境变量尚未定义,则整个命令块已经被预处理,并分别用环境变量中的适当字符串替换了所有出现的。并且可以看出,当整个命令块中的下一个命令行由 Windows 命令解释器执行时,%ecb%和的值%bdf%永远不会再次修改。

更紧凑的是:

@echo off
setlocal EnableExtensions EnableDelayedExpansion
rem ...
if "%$ecbId%" == "" (
    echo Enter '1' to blabla
    echo Enter '2' to blabla
    echo Enter anything else to abort.
    echo/
    set "UserChoice=abort"
    set /P "UserChoice=Type input: "
    if "!UserChoice!" == "1" (
        echo toto1
        rem save into property file
        >>MDC.properties echo currentMaster=%ecb%
        >>MDC.properties echo masterDcName=ECBDC
        >>MDC.properties echo currentClone=%bdf%
        >>MDC.properties echo cloneDcName=BDFDC
    ) else if "!UserChoice!" == "2" (
        echo toto2
        rem save into property file
        >>MDC.properties echo currentMaster=%bdf%
        >>MDC.properties echo masterDcName=BDFDC
        >>MDC.properties echo currentClone=%ecb%
        >>MDC.properties echo cloneDcName=ECBDC
    ) else (
        echo Unknown input ... Aborting script
        endlocal
        exit /B 400
    )
)
endlocal
Run Code Online (Sandbox Code Playgroud)

另一种解决方案是使用choice自 Windows Vista 以来默认可用的命令。它的优点是用户只需要按三个键之一。在这种情况下不需要延迟的环境变量扩展,因为choice退出时的值取决于分配给环境变量的按键,errorlevel即使在命令块内也可以在没有延迟扩展的情况下进行评估。

@echo off
rem ...
if "%$ecbId%" == "" (
    echo Enter '1' to blabla
    echo Enter '2' to blabla
    echo Enter 'A' to abort.
    echo/
    %SystemRoot%\System32\choice.exe /C 12A /N /M "Type input:"
    if errorlevel 3 (
        echo Aborting script
        exit /B 400
    ) else if errorlevel 2 (
        echo toto2
        rem save into property file
        >>MDC.properties echo currentMaster=%bdf%
        >>MDC.properties echo masterDcName=BDFDC
        >>MDC.properties echo currentClone=%ecb%
        >>MDC.properties echo cloneDcName=ECBDC
    ) else (
        echo toto1
        rem save into property file
        >>MDC.properties echo currentMaster=%ecb%
        >>MDC.properties echo masterDcName=ECBDC
        >>MDC.properties echo currentClone=%bdf%
        >>MDC.properties echo cloneDcName=BDFDC
    )
)
Run Code Online (Sandbox Code Playgroud)

由于字符被指定为选项后的第一个字符,用户必须按1导致choice以值退出,或让以值退出的键或键(不区分大小写)导致以值退出,因为字符被指定为第三个字符在 option 之后的下一个参数字符串中。额外确认后,可以额外按下退出批处理文件处理。所有其他按键都被忽略并导致输出铃声,通知用户按下了错误的键。11/C2choice2Achoice3A/CCtrl+Cchoice

现在最好choice只使用文件名或完全限定的文件名对此类用户提示使用该命令,以获得最大的执行安全性。