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

3

一个.bat文件应该将用户输入与一个值进行比较。当我使用1或2作为输入进行测试时,它并没有进入IF "%choice%"=="1"

以下是批处理文件:

@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
        )
    )
)

命令输出结果:
Enter '1' to blabla
Enter '2' to blabla
Enter anything to abort
Type input: 1
toto3
toto4
Unknown input ... Aborting script

为什么它没有进入“IF“%choice%”==”1“”条件中?
2个回答

10

以下是一份正常运行批处理代码:

@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

延迟扩展通过命令setlocal EnableExtensions EnableDelayedExpansion在批处理文件顶部启用。命令SETLOCAL不仅启用了延迟扩展,还将整个命令进程环境保存在堆栈上,如我在此回答中详细解释的那样。在执行命令ENDLOCAL时,命令进程环境将从堆栈中恢复,这也可以由Windows命令处理器在退出批处理文件处理时隐式完成,而无需显式执行endlocal

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

当Windows命令处理器到达第四行时,从第4行到倒数第二行的整个命令块都将被解析和预处理,然后才会评估IF条件。在预处理步骤中,从第4行到第36行的整个代码块中使用百分号引用的所有环境变量都将被扩展。

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

仅使用延迟扩展引用的环境变量UserChoice在解析/预处理整个命令块时不会被扩展。

因此,可以将默认值abort分配给环境变量UserChoice,以备批处理用户只按下RETURNENTER而未输入任何内容的情况。

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

可以通过以下方法轻松观察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

另一个解决方案是使用自Windows Vista以来默认可用的choice命令。它的优点是用户只需要按下三个键中的一个即可。在这种情况下,不需要延迟环境变量扩展,因为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
    )
)
用户必须按下1,导致choice以值1退出,因为在选项/C之后指定了字符1作为第一个字符,或者按下2choice以值2退出,或者按下不区分大小写的A键,结果choice会以值3退出,因为在选项/C之后的下一个参数字符串中指定了字符A作为第三个字符。此外,还可以按Ctrl+C来退出批处理文件处理,需要额外确认。其他所有按键都将被choice忽略,并且将发出响铃声以通知用户按下了错误的键。
现在最好使用命令choice进行此类用户提示,只需使用文件名或完全限定的文件名即可获得最大的执行安全性。

感谢您提供详细信息! - Cedric

3
以下代码可能对您有所帮助。
@echo off
echo Enter '1' to blabla
echo Enter '2' to blabla
echo Enter anything to abort
set /p choice="Type input: "

IF "%choice%"=="1" goto echoto1         

rem enter anything here you want it to do if variable is not met

:echoto1
rem start's here if the variable was met
echo toto1
@echo currentMaster=%ecb%>> MDC.properties
@echo masterDcName=ECBDC>> MDC.properties
@echo currentClone=%bdf%>> MDC.properties
@echo cloneDcName=BDFDC>> MDC.properties

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