如果比较表现出乎意料

时间:2014-09-21 01:03:36

标签: batch-file

我在Windows 7下运行以下批处理程序:

@echo off
setlocal EnableExtensions EnableDelayedExpansion
set iterations=2
set flag=1
echo Beginning at !TIME! with iterations %iterations%
for /l %%x in (1, 1, %iterations%) do (
  echo   Starting iteration %%x at !TIME!
  REM flag 
  if %flag%==0 do (
    echo       flag=%flag% and flag=0
  )
  if %flag%==1 do (
    echo       flag=%flag% and flag=1
  )
)

我期待上面的程序发出与此类似的输出:

C:\Temp>test.bat
Beginning at 17:47:48.18 with iterations 2
  Starting iteration 1 at 17:47:48.18
      flag=1 and flag=1
  Starting iteration 2 at 17:47:48.18
      flag=1 and flag=1
C:\Temp>

但我看到的是:

C:\Temp>test.bat
Beginning at 17:47:48.18 with iterations 2
  Starting iteration 1 at 17:47:48.18
      flag=1 and flag=0
  Starting iteration 2 at 17:47:48.18
      flag=1 and flag=0
'do' is not recognized as an internal or external command,
operable program or batch file.
      flag=1 and flag=1
C:\Temp>

我不明白为什么:

  1. 第一个' if'条款永远执行......' flag'等于1,按照我的思维方式,这个字符串比较应该返回false
  2. 两者' if'在第二次迭代期间执行子句
  3. 第二次执行循环会产生与' do'
  4. 相关的错误消息

    有趣的是,如果我在第二个'中强制进行字符串比较,我可以删除与' do'相关的错误消息,即

    @echo off
    setlocal EnableExtensions EnableDelayedExpansion
    set iterations=2
    set flag=1
    echo Beginning at !TIME! with iterations %iterations%
    for /l %%x in (1, 1, %iterations%) do (
      echo   Starting iteration %%x at !TIME!
      if %flag%==0 do (
        echo       flag=%flag% and flag=0
      )
      if "%flag%"==1 do (
        echo       flag=%flag% and flag=1
      )
    )
    

    然后产生这个输出:

    C:\Temp>test.bat
    Beginning at 17:58:27.09 with iterations 2
      Starting iteration 1 at 17:58:27.10
          flag=1 and flag=0
      Starting iteration 2 at 17:58:27.10
          flag=1 and flag=0
          flag=1 and flag=1
    C:\Temp>
    

    但即使我发现令人费解:

    1. 为什么强制进行字符串比较(我还是进行字符串比较)会消除“做”字符串比较。错误?

4 个答案:

答案 0 :(得分:3)

 if %flag%==1 do (
    echo       flag=%flag% and flag=1
  )

这应该是

 if !flag!==1  (
    echo       flag=!flag! and flag=1
  )

DO来自for循环。从if条件中删除它。在延迟expasnion下更好地使用!访问我们的变量

答案 1 :(得分:3)

npocmaka found the source of your problem - DO不与IF语句一起使用。

剩下的就是解释错误代码的输出。

您的IF语句都应位于带括号的FOR DO块中。但是每个IF语句中的DO被视为外部命令(不存在),(被视为DO命令的参数 - 因此它不会启动新块!

你的第一个IF语句永远不会成立,因此它永远不会尝试执行DO (,所以没有错误。您的下一个ECHO始终执行,然后)关闭FOR / F循环!

一旦你的循环完成所有迭代,就会执行下一个IF语句,这次是TRUE。因此它会尝试执行DO (并引发错误,因为DO不是有效命令。

然后执行下一个ECHO,忽略最后的)。这是批量解析的一个特殊怪癖。当解析器正在查找命令,并且它不在括号内的块中,并且它找到)时,它只是忽略)。不仅如此,它还忽略了其余部分。

因此,只要不出现在带括号的块中,就可以将以下内容视为无害的注释

) This is effectively a comment, as long as not within a parenthesized block

以下是带有修改缩进的代码,以显示解析器如何看到它。

@echo off
setlocal EnableExtensions EnableDelayedExpansion
set iterations=2
set flag=1
echo Beginning at !TIME! with iterations %iterations%
for /l %%x in (1, 1, %iterations%) do (
  echo   Starting iteration %%x at !TIME!
  REM flag
  if %flag%==0 do (
  echo       flag=%flag% and flag=0
)
if %flag%==1 do (
echo       flag=%flag% and flag=1

当您在第二个IF语句中添加引号时,您只在一方执行此操作。它将"1"1进行比较,它始终为FALSE,因此它永远不会尝试执行DO (,也不会生成错误。如果您向双方添加了引号,那么您将再次收到错误:

if "%flag%"=="1" do (

答案 2 :(得分:1)

delayedexpansion的第6,886代。

在块语句(a parenthesised series of statements)中,解析整个块并执行然后。块中的任何%var%将在解析块时被该变量的值替换 - 在块执行之前 - 同样的事情适用于{{1 }}

因此,FOR ... DO (block)将在遇到IF (something) else (somethingelse)时使用%variables%的值执行。

解决此问题的两种常见方法是1)使用IF并使用setlocal enabledelayedexpansion代替!var!来访问已更改的%var%或2}值以进行调用一个子程序,用于使用更改的值执行进一步处理。

另请注意使用显示var更改值的CALL ECHO %%var%%var显示,但遗憾的是RESETS错误级别。

如果涉及旗帜,情况会再次发生变化。 CALL ECHO %%errorlevel%%将确保清除标记。 set "flag="将确保设置(值不相关。)使用set "flag=somethingelse"适用于if defined flag (doiftrue) else (doiffalse)运行时(当前)状态 - 而不是分析时间值。

flag语法可确保批处理行上的任何尾随空格不包含在分配给set "var=value"的值中。


"强制"字符串比较。

嗯,你需要小心。在var语句中,如果两个参数都是语法有效整数,那么将执行比较,就好像字符串是整数一样。这甚至发生在字符串超出所使用的32位变量的范围内。这可能导致奇怪的结果:

if

要强制进行字符串比较,您需要确保比较的值不是@ECHO OFF SETLOCAL SET /a maxint=2147483647 FOR %%v IN (10, 010, 012, 0xa ) DO CALL :evaluate %%v ECHO ============= FOR %%v IN (2147483647, 2147483646, 2147483648, 21464836470) DO CALL :evaluate2 %%v ECHO ============= FOR %%v IN (2147483647, 2147483646, 2147483648, 21464836470) DO CALL :evaluate2 k%%v ECHO ============= IF "987" lss "1234" (ECHO "987" is less than "1234") ELSE (ECHO "987" is NOT less than "1234") IF "987" gtr "1234" (ECHO "987" is greater than "1234") ELSE (ECHO "987" is NOT greater than "1234") GOTO :EOF :evaluate ECHO comparing %1 to 10 IF %1==10 (ECHO %1 is equal to 10) ELSE (ECHO %1 is NOT equal to 10) IF "%1"=="10" (ECHO "%1" is equal to "10") ELSE (ECHO "%1" is NOT equal to "10") GOTO :eof :evaluate2 ECHO comparing %1 to k%maxint% IF %1 equ k%maxint% (ECHO %1 is equal to k%maxint%) ELSE (ECHO %1 is NOT equal to k%maxint%) IF %1 lss k%maxint% (ECHO %1 is less than k%maxint%) ELSE (ECHO %1 is NOT less than k%maxint%) IF %1 gtr k%maxint% (ECHO %1 is greater than k%maxint%) ELSE (ECHO %1 is NOT greater than k%maxint%) GOTO :eof 规则中的有效数值常量。 Clasically,用引号括起来。请注意,cmd1 不是相同的字符串。

另一个可能引起粗心大意的问题是字符串比较是从左到右进行的,因此尝试使用字符串比较数值并不像看起来那么简单。

答案 3 :(得分:0)

好的,我现在对批处理解析器的工作方式有了更深入的了解,谢谢您的详细信息。

首先,我的错误在于使用'do'来扩展'if'子句。更正, 和功能,代码如下:

@echo off setlocal EnableExtensions EnableDelayedExpansion set iterations=2 set flag=1 echo Beginning at !TIME! with iterations %iterations% for /l %%x in (1, 1, %iterations%) do ( echo Starting iteration %%x at !TIME! if %flag%==0 ( echo flag=%flag% and flag=0 ) if %flag%==1 ( echo flag=%flag% and flag=1 ) )

这会产生预期的输出:

C:\Temp>test.bat
Beginning at  7:12:58.81 with iterations 2
  Starting iteration 1 at  7:12:58.82
      flag=1 and flag=1
  Starting iteration 2 at  7:12:58.82
      flag=1 and flag=1

C:\Temp>

感谢您的协助,

- SK