簡體   English   中英

Windows Batch:findstr未在for循環中設置錯誤級別

[英]Windows Batch: findstr not setting ERRORLEVEL within a for loop

誰能向我解釋以下代碼段為何打印0

@echo off
setlocal

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

在for循環外添加另一個等效語句使它打印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的新手,因此這對我來說有點神秘,因為這兩個語句似乎無關。 任何幫助,將不勝感激,謝謝!

問題在於,在code block (帶括號的語句系列)中,任何%var%都將在parse time由變量的實際值替換。

在您的第一個示例中, %errorlevel% 0並以此類推。 在第二個示例中,當遇到for時為1 ,因此將其替換為1

如果要顯示可能在循環中更改的環境變量的值,則需要執行以下三種操作之一:

  1. 調用setlocal enabledelayedexpansion並回顯!var! 而不是%var% -注意您可以激活的嵌套setlocal指令的數量是有限的。

  2. 調用子程序

  3. 使用語法漏洞。

關於SO的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
    )
)

請注意,這將查看錯誤errorlevelrun-time時值,並且if errorlevel n表示“如果錯誤級別為n 或大於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 (此處的冒號很重要-這意味着“轉到文件的實際結尾”

或者,使用子例程的特殊版本-語法漏洞

@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 echo %%errorlevel%%
)

更新:

如果在for語句的)結束echo %ERRORLEVEL%之后放置echo %ERRORLEVEL%語句,則它會按預期工作。

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

由於確切的“確定性”原因,我有點在這里猜測,但setlocal肯定是造成它的原因。 我假設for循環中ERRORLEVEL的值是cmd /c ...語句的結果。 這似乎是一種奇怪的行為,但是我在批處理文件中看到的情況更糟。

如果您在一開始就刪除setlocal ,則它會像通常期望的那樣工作。

原版的:

findstr在運行時設置錯誤errorlevel 因此, echo %errorlevel%將始終輸出0。

例如,以下findstr語句成功,因此它輸出具有匹配項的行(唯一傳遞給它的行):

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

雖然此語句不成功(findstr不輸出任何內容),並且錯誤級別設置為1:

C:\>echo blah_in | findstr bin
C:\>echo %errorlevel%
1            ; failed
@echo off
setlocal ENABLEDELAYEDEXPANSION

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

這應該工作。

cmd中的for循環從技術上講是一行,因此該變量僅擴展一次。 使用感嘆號代替百分號和“ ENABLEDELAYEDEXPANSION”應該可以解決它。

@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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM