Windows Batch:findstr未在for循环中设置ERRORLEVEL

时间:2016-03-26 16:15:18

标签: windows batch-file cmd windows-10 command-prompt

有人可以向我解释为什么以下代码段打印0

@echo off
setlocal

for /f %%i in ('cmd /c echo blah') do (
    echo %%i | findstr bin > NUL
    echo %ERRORLEVEL%
)

在for-loop之外添加另一个等效语句时,会打印1 1

@echo off
setlocal

echo blah | findstr bin > NUL
echo %ERRORLEVEL%

for /f %%i in ('cmd /c echo blah') do (
    echo %%i | findstr bin > NUL
    echo %ERRORLEVEL%
)

我对Batch有点新手,所以这对我来说有点神秘,因为这两个陈述似乎是无关的。任何有关这方面的帮助将不胜感激,谢谢!

4 个答案:

答案 0 :(得分:5)

问题是,在code block(带括号的系列语句)中,任何%var%都会被parse time处变量的实际值所取代。

欣赏您的第一个示例,%errorlevel% 0,并如此回应。在第二个示例中,遇到1时为for,因此它被1替换。

如果要显示可在循环中更改的环境变量的值,则需要执行以下三项操作之一:

  1. 调用setlocal enabledelayedexpansion并回显!var!而不是%var% - 注意您可以激活的嵌套setlocal指令数量有限。

  2. 调用子程序

  3. 使用syntax-exploit。

  4. 关于delayedexpansion的论文有很多很多文章。

    粗略地说,您可以简单地使用(注意 - 案例在批处理中基本上无关紧要,除了循环控制变量的情况(在此实例中为metavariable - %%i

    @echo off
    setlocal ENABLEDELAYEDEXPANSION
    
    echo blah | findstr bin > NUL
    echo %ERRORLEVEL% or !errorlevel!
    
    for /f %%i in ('cmd /c echo blah') do (
        echo %%i | findstr bin > NUL
        echo %ERRORLEVEL% or !errorlevel!
    )
    

    另一种方法是动态调用setlocal

    @echo off
    setlocal
    
    echo blah | findstr bin > NUL
    echo %ERRORLEVEL% or !errorlevel!
    for /f %%i in ('cmd /c echo blah') do (
        echo %%i | findstr bin > NUL
        setlocal ENABLEDELAYEDEXPANSION
        echo %ERRORLEVEL% or !errorlevel!
        endlocal    
    )
    

    这样做的缺点是endlocal支持自上次setlocal以来对环境所做的任何更改。另请注意,如果delayedexpansion无效,则!不再是特殊字符。

    或者,您可以以传统方式使用errorlevel

    @echo off
    setlocal
    
    echo blah | findstr bin > NUL
    echo %ERRORLEVEL% or !errorlevel!
    
    for /f %%i in ('cmd /c echo blah') do (
        echo %%i | findstr bin > NUL
        if errorlevel 1 (echo errorlevel is 1 or greater
        ) else (echo errorlevel is 0
        )
    )
    

    请注意,这会查看run-time的{​​{1}}值,errorlevel表示"如果errorlevel为n 或大于n &#34 ;

    或 - 调用子程序:

    if errorlevel n

    请注意@echo off setlocal echo blah | findstr bin > NUL echo %ERRORLEVEL% or !errorlevel! for /f %%i in ('cmd /c echo blah') do ( echo %%i | findstr bin > NUL call :show ) goto :eof :show echo %ERRORLEVEL% goto :eof (此处冒号很重要 - 这意味着"转到物理文件末尾"

    或者,使用子程序的特殊版本 - 语法 - 利用

    goto :eof

答案 1 :(得分:0)

更新:

如果您在for语句的结束echo %ERRORLEVEL%之后放置)语句,它将按预期工作。

for /f %%i in ('cmd /c echo blah') do (
    echo %%i | findstr bin > NUL
)
echo %ERRORLEVEL%

我有点猜测,因为确切的'确定'原因,但setlocal肯定会导致它。我假设for循环中的ERRORLEVEL值是cmd /c ...语句的结果。这似乎是一种奇怪的行为,但我在批处理文件中看到的情况更糟。

如果您在开头删除了setlocal,它就会像通常期望的那样工作..

ORIGINAL:

findstr在运行时设置errorlevel。因此,echo %errorlevel%将始终输出0.

例如,以下findstr语句成功,因此它输出带匹配的行(传递给它的唯一行):

C:\>echo blahbin | findstr bin
blahbin
C:\>echo %errorlevel%
0            ; success / found it..

虽然此语句不成功(findstr不输出任何内容),并且errorlevel设置为1:

C:\>echo blah_in | findstr bin
C:\>echo %errorlevel%
1            ; failed

答案 2 :(得分:0)

@echo off
setlocal ENABLEDELAYEDEXPANSION

for /f %%i in ('cmd /c echo blah') do (
    echo %%i | findstr bin > NUL
    echo !ERRORLEVEL!
)

这应该有效。

cmd中的for循环在技术上是一行,因此变量只展开一次。使用感叹号而不是百分比和" ENABLEDELAYEDEXPANSION"应该解决它。

答案 3 :(得分:0)

@echo off
setlocal enabledelayedexpansion

:::This part is not exactly necessary but it creates the file dictionary.txt and the var - this
echo.word>dictionary.txt
set this=notaword

:::So instead of checking for errorlevel just check for a space in the output
:middle
for /f "tokens=*" %%a in ('findstr /n /b /c:%this% dictionary.txt') do (
  set "output=%%a"
  goto :yeah
)
:yeah
if exist %output% (""," "
    goto :oops
) else (
    goto :nice
)
:oops
echo.%this% is NOT a word
pause & set this=word & goto :middle
:nice
echo.%output%
pause