簡體   English   中英

FOR /F 命令循環中的 ERRORLEVEL 返回意外結果

[英]ERRORLEVEL in FOR /F Command Loop Returns Unexpected Result

我正在嘗試記錄net stop的輸出,同時還捕獲其ERRORLEVEL

基於這個問題,我在嵌套子程序中嘗試了以下操作:

set /a loopIndex=0
for /F "usebackq delims=" %%i in (`net stop %SERVICE_NAME%`) do (
 if !loopIndex! EQU 0 if !errorlevel! EQU 1 set statementError=1
 set /a loopIndex+=1
 call :logMessage "%%i"
)
echo statementError: %statementError%

但是,這不起作用,即使net stop成功也會拋出1

如果沒有臨時文件,這可能嗎? 如果沒有,臨時文件解決方案會是什么樣子?

正如@drruruu 在這個問題中所問的:

如果沒有臨時文件,這可能嗎?

是的,沒有臨時文件也是可能的。 通過將 ERRORLEVEL 發送到 IN 子句中的 STDOUT 並在 LOOP 子句中解析它。 它也可以通過延遲擴展來完成

為方便起見,這里有一個例子。 它有點像 FINDSTR 包裝器,用於在批處理本身中搜索字符串。 它涵蓋了您需要知道哪里出了問題、哪里出了問題以及為什么出了問題的所有常見情況:

  • DO() 子句中的錯誤(又名循環)並獲取相應的退出代碼
  • IN() 子句出錯並得到相應的退出碼
  • 直接在 FOR 子句處出錯(錯誤的語法、錯誤的分隔符等)

以下腳本使用 FINDSTR 和標志作為參數模擬這些情況:

  • 第一個參數是要搜索的字符串。
  • 第二個參數是一個 0/​​1 標志,用於模擬循環中與 FINDSTR 無關的錯誤。
  • 第三個參數是一種模擬 FOR 子句本身錯誤的方法(不是 IN 也不是 LOOP)
  • 第四個參數是一種測試 FINDSTR 的方法,當要搜索的文件不存在時,該 FINDSTR 退出 255。 可悲的是,FINDSTR 在文件/文件中找不到字符串時以 1 退出,但在找不到任何文件時也以 1 退出。 使用第四個參數,我們模擬 FINDSTR 以 255 退出的情況,當它找不到文件。
    @echo off
    SETLOCAL ENABLEEXTENSIONS
    IF ERRORLEVEL 1 (
      ECHO Can't use extensions
      EXIT /B 1
    )    
    SETLOCAL ENABLEDELAYEDEXPANSION
    IF ERRORLEVEL 1 (
      ECHO Can't use delayed expansion 
      EXIT /B 1
    )
    REM The string to search
    SET "LOCALV_STRING=%1"
    REM The file to search. Myself.
    SET "LOCALV_THIS=%0"
    REM Store the exit code for the LOOP
    SET "LOCALV_ERR="
    REM Store the exit code for the IN
    SET "LOCALV_RET="
    REM Flag to stop parsing output for error simulation
    SET "LOCALV_END="
    REM To get the exit code of the IN clause, we get it through expansion with a second FOR loop using the well known CALL expansion and send it on STDOUT in the form "__<code>"
    FOR /F "%~3tokens=*" %%M IN ('FINDSTR "!LOCALV_STRING!" "!LOCALV_THIS%~4!" ^
                               ^& FOR /F %%A IN ^("ERRORLEVEL"^) DO @CALL ECHO __%%%%A%%') DO (
      SET "LOCALV_TMP=%%~M"
      REM Simulate that something goes wrong with FINDSTR I/O
      IF NOT EXIST "!LOCALV_THIS!%~4" ( 
        SET "LOCALV_RET=255"
        SET LOCALV_END=1
      )
      IF "!LOCALV_END!" == "" (
        REM SImulate a problem in the loop
        IF "%2" == "1" (
          (CMD /C EXIT /B 127)
          SET LOCALV_END=1
        ) ELSE (
          IF NOT "!LOCALV_TMP:~0,2!" == "__" ECHO Found: !LOCALV_TMP!
        )
      )
      IF "!LOCALV_TMP:~0,2!!LOCALV_RET!" == "__" SET "LOCALV_RET=!LOCALV_TMP:__=!"
    )
    SET "LOCALV_ERR=!ERRORLEVEL!"
    REM LOCALV_ERR get the exit code from the last iteration of the for loop
    REM LOCALV_RET get the exit code from the IN command of the for loop
    REM Sadly, FINDSTR exit with 1 if it did not find the string, but also with 1 if it could not found the file. To simulate a proper handling of exit code for 
    REM abnormal hardware/software situation, %2 is used to force a 255 exit code
    REM If LOCALV_RET is not defined, this means the FOR...ECHO__.. wasn't executed, therefore there is a pb with the FOR LOOP
    IF "!LOCALV_RET!" == "" (
      ECHO Something went wrong with FOR...
      EXIT /B 1
    )
    REM If LOCALV_RET is defined, this means the FOR...ECHO__.. was executed and the last loop operation has parsed the FINDSTR exit code, LOCALV_RET get its exit code
    REM If LOCALV_RET is defined but LOCALV_ERR is not "0", something went wrong in the loop (I/O error, out of memory, wathever you could think), the problem is not FINDSTR
    IF NOT "!LOCALV_ERR!" == "0" (
      ECHO Error in the loop while searching "!LOCALV_STRING!" in "!LOCALV_THIS!", exit code !LOCALV_RET!. Loop exit code : !LOCALV_ERR!.
      EXIT /B 4
    )
    REM If LOCALV_RET is "0", FINDSTR got matching strings in the file, if "1", FINDSTR don't find any matching string, if anything else, FINDSTR got a problem like failed I/O.
    REM If LOCALV_RET is "0" and LOCALV_ERR is "0", everything is ok.
    IF "!LOCALV_RET!" == "0" (
      ECHO Success.
      EXIT /B 0
    )
    REM If LOCALV_RET is "1" and LOCALV_ERR is "0", FINDSTR failed to find the string in the file "or" failed to find file, for the latter we simulate that FINDSTR exit with 255 .
    IF "!LOCALV_RET!" == "1" (
      ECHO FINDSTR failed to find "!LOCALV_STRING!" in "!LOCALV_THIS!", exit code !LOCALV_RET!. Loop exit code : !LOCALV_ERR!.
      EXIT /B 2
    )
    REM If LOCALV_RET isn't "0" nor "1" and LOCALV_ERR is "0", FINDSTR failed to do the job and LOCALV_RET got the exit code.
    ECHO FINDSTR: Houst^W OP, we've got a problem here while searching "!LOCALV_STRING!" in "!LOCALV_THIS!", exit code !LOCALV_RET!. Loop exit code : !LOCALV_ERR!.
    EXIT /B 3

腳本輸出:

  1. 正常運行,無錯誤模擬。
PROMPT>.\for.bat FOR 0 "" ""
Found: FOR /F "%~3tokens=*" %%M IN ('FINDSTR "FOR" ""
Found: ^& FOR /F %%A IN ^("ERRORLEVEL"^) DO @CALL ECHO __%%%%A%%') DO (
Found: REM If LOCALV_RET is not defined, this means the FOR...ECHO__.. wasn't executed, therefore there is a pb with the FOR LOOP
Found: ECHO Something went wrong with FOR...
Found: REM If LOCALV_RET is defined, this means the FOR...ECHO__.. was executed and the last loop operation has parsed the FINDSTR exit code, LOCALV_RET get its exit code
Success.
  1. 正常運行,沒有錯誤模擬,帶有FINDSTR在文件中找不到的字符串。
PROMPT>.\for.bat ZZZ 0 "" ""
FINDSTR failed to find "ZZZ" in ".\for.bat", exit code 1. Loop exit code : 0.
  1. 模擬 LOOP 子句中的錯誤,與 FINDSTR 無關。
PROMPT>.\for.bat FOR 1 "" ""
Error in the loop while searching "FOR" in ".\for.bat", exit code 0. Loop exit code : 127.
  1. 使用未知的“delimstoken”選項在開始時模擬 FOR 子句中的錯誤。
PROMPT>.\for.bat FOR 0 "delims" ""
delimstokens=*" was unexpected.
Something went wrong with FOR...
  1. 如果找不到文件,模擬 FINDSTR 退出 255。
PROMPT>.\for.bat FOR 1 "" "ERR"
FINDSTR : Can't open
FINDSTR: HoustW OP, we've got a problem here while searching "FOR" in ".\for.bat", exit code 255. Loop exit code : 0.

FOR /F 命令在新的 cmd.exe 進程中執行 NET STOP。 FOR /F 處理標准輸出,僅此而已。 主腳本無法看到 FOR /F 命令可能創建的任何變量值,因為一旦子進程終止,它們就會消失。

最簡單和最有效的解決方案使用臨時文件。 我假設 NET STOP 有兩種可能的錯誤代碼 - 成功 = 0 和錯誤 = 1。因此,最簡單的解決方案是在出現錯誤時簡單地創建一個臨時錯誤信號文件。

下面以一種通用的方式演示了這個概念:

@echo off
del error.flag 2>nul
for /f "delims=" %%A in ('net stop %SERVICE_NAME% ^|^| echo error>error.flag') do (
  ...
)
if exist error.flag (
  echo There was an error
  del error.flag
)

如果需要,您可以輕松地將錯誤測試放在 DO() 代碼中。

雖然@dbenham 的答案適用於%ERRORLEVEL%返回二進制值的情況,但我無法確認或否認返回的net stop退出代碼實際上是二進制的,因此選擇了 n 進制解決方案。

根據@dbenham 的DOS 提示論壇帖子

FOR /F "delims=" %%i IN ('net stop MyService 2^>^&1 ^& CALL ECHO %%^^ERRORLEVEL%%^>error.level') DO (
 CALL :logMessage "%%i"
)
FOR /F "delims=" %%i IN (error.level) DO (SET /A statementError=%%i)
DEL error.level
IF %statementError% NEQ 0 ()

分解語句解析:

net stop MyService 2^>^&1 ^& CALL ECHO %%^^ERRORLEVEL%%^>error.level
net stop MyService 2>&1 & CALL ECHO %^ERRORLEVEL%>error.level
echo %ERRORLEVEL%>error.level

在這里, CALL專門用於延遲解析%ERRORLEVEL%直到執行ECHO

暫無
暫無

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

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