Установить уровень ошибок в пакетном файле Windows

Я пишу пакет script, который будет проходить через каждую строку текстового файла (каждая строка, содержащая имя файла), проверьте, существует ли файл, а затем запускает файл и перемещает его.

Вот моя партия script:

REM Loop through each line of input.txt
FOR /F "tokens=1-3 delims=, " %%i IN (./ready/input.txt) DO (
  ECHO.
  ECHO.
  ECHO.
  ECHO Check %%i exists, set error flag if it doesnt
  if not exist .\ready\%%i set errorlevel=2
echo return code is %errorlevel%

  ECHO Run %%i if it exists
  if errorlevel 0 call .\ready\%%i

  ECHO Move %%i to archive if no error occured 
  if errorlevel 0 copy .\ready\%%i .\archive\%mydate%_%mytime%_%%j_%%k_%%i

  ECHO Copy line of text to the new output.txt file if an error occured
  if %errorlevel% NEQ 0 >>output.txt %%i, %%j, %%k
)

Вот результат: enter image description here

Я не понимаю, почему "if errorlevel" работает не так, как ожидалось... если файл не существует (как в этом примере, где он не существует), он НЕ должен запускать файл, он НЕ должен скопируйте файл, и он должен повторить 2, а не 0

Изменить 1. Я читал еще одну SO Post относительно "задержки изменения переменной среды". Я не уверен, связана ли эта проблема

Ответы

Ответ 1

@ECHO OFF
SETLOCAL
DEL output.txt 2>nul
REM Loop through each line of input.txt
FOR /F "tokens=1-3 delims=, " %%i IN (.\ready\input.txt) DO (
  ECHO.
  ECHO.
  ECHO.
  ECHO Check %%i exists, set error flag if it doesnt
  if exist .\ready\%%i (set "errorflag=") ELSE (set errorflag=2)
CALL echo return code is %%errorflag%%

  ECHO Run %%i if it exists
  if NOT DEFINED errorflag (
   call .\ready\%%i
   ECHO Move %%i to archive if no error occured
   if errorlevel 1 (SET errorflag=3) ELSE (ECHO copy .\ready\%%i .\archive\%mydate%_%mytime%_%%j_%%k_%%i)
  )
  ECHO Copy line of text to the new output.txt file if an error occured
  if DEFINED errorflag >>output.txt ECHO %%i, %%j, %%k
)
GOTO :EOF

Здесь перезаписана процедура.

Примечание: output.txt удаляется в начале, иначе >> будет добавляться в любой существующий файл. 2>nul подавляет сообщения об ошибках, если сбой удаления (например, файл не существует)

В блочном выражении (a parenthesised series of statements) выполняется анализ блока ENTIRE и выполняется THEN. Любой %var% внутри блока будет заменен на это значение переменной В ВРЕМЯ БЛОК ПРОВЕРЕН - до того, как будет выполнен блок.

Следовательно, IF (something) else (somethingelse) будет выполняться с использованием значений %variables% во время встречи IF.

Два общих способа преодоления: 1) использовать setlocal enabledelayedexpansion и использовать !var! вместо %var% для доступа к полученному значению var или 2) для вызова подпрограммы для дальнейшей обработки с использованием измененные значения.

Обратите внимание, что использование CALL ECHO %%var%%, которое отображает измененное значение var. CALL ECHO %%errorlevel%% отображает, но, к сожалению, RESETS errorlevel.

IF DEFINED var истинно, если var имеет значение СОВЕТ.

ERRORLEVEL - это специальное имя переменной. Он задается системой, но если он задан пользователем, назначенное пользователем значение переопределяет системное значение.

IF ERRORLEVEL n ИСТИНА, если ERRORLEVEL n ИЛИ БОЛЬШЕ, чем n. IF ERRORLEVEL 0 всегда верно.

Синтаксис SET "var=value" (где значение может быть пустым) используется для обеспечения того, чтобы любые свободные пробелы в конце строки НЕ включались в назначенное значение.

Для целей тестирования требуемые команды просто ECHO ed. После того, как вы подтвердите правильность команд, измените ECHO COPY на COPY, чтобы фактически скопировать файлы.

Я использовал следующий input.txt:

seterr1.bat, J1, K1
seterr5.bat,J2,K2
seterr0.bat,J3 K3
seterr5.bat, J4, K4
notexist.bat, J5, K5

С существующими файлами seterr*.bat, которые содержат

@ECHO OFF
EXIT /b 1

(где 1 в последней строке определяет возвращаемый ERRORLEVEL)

и получил результат:

Check seterr1.bat exists, set error flag if it doesnt
return code is 
Run seterr1.bat if it exists
Move seterr1.bat to archive if no error occured
Copy line of text to the new output.txt file if an error occured

Check seterr5.bat exists, set error flag if it doesnt
return code is 
Run seterr5.bat if it exists
Move seterr5.bat to archive if no error occured
Copy line of text to the new output.txt file if an error occured

Check seterr0.bat exists, set error flag if it doesnt
return code is 
Run seterr0.bat if it exists
Move seterr0.bat to archive if no error occured
copy .\ready\seterr0.bat .\archive\__J3_K3_seterr0.bat
Copy line of text to the new output.txt file if an error occured

Check seterr5.bat exists, set error flag if it doesnt
return code is 
Run seterr5.bat if it exists
Move seterr5.bat to archive if no error occured
Copy line of text to the new output.txt file if an error occured

Check notexist.bat exists, set error flag if it doesnt
return code is 2
Run notexist.bat if it exists
Copy line of text to the new output.txt file if an error occured

Обратите внимание, что COPY просто ECHO ed, как я упоминал ранее.

и output.txt

seterr1.bat, J1, K1
seterr5.bat, J2, K2
seterr5.bat, J4, K4
notexist.bat, J5, K5

Ответ 2

ERRORLEVEL и %ERRORLEVEL% - две разные переменные. Это означает, что ваш код с echo return code is %errorlevel% и if %errorlevel% NEQ 0 >>output.txt %%i, %%j, %%k, вероятно, неверен.

ERRORLEVEL встроен и используется для получения результата последней команды. Вы можете использовать его как:

IF ERRORLEVEL 1 ECHO error level is 1 or more

ERRORLEVEL не может быть установлен, так же как bash не позволяет вам set ?= ...

%ERRORLEVEL% является переменной среды. Если %ERRORLEVEL% задано, то оно используется в вашем script при использовании %ERRORLEVEL%. Если %ERRORLEVEL% не задано И, если расширения команд включены, то он возвращается к ERRORLEVEL. ERRORLEVEL не обновляет %ERRORLEVEL%.

У Раймонда Чена есть хорошая запись в блоге: ERRORLEVEL is not %ERRORLEVEL%. Часть контента в этом ответе была бесстыдно снята с него.

Ответ 3

Используйте что-то вроде следующей подпрограммы:

:return
   ECHO @exit /b %1 >ret.cmd
   CALL ret.cmd
   GOTO :eof

Затем используйте его следующим образом:

:Attempt
   SETLOCAL
   CALL somethingThatFails
   SET retcode=!errorlevel!
   CALL somethingThatPasses : don't care about the errorlevel here
   CALL :return !retcode!
   ENDLOCAL
   CALL :eof

Итак, все это выглядело бы так:

test.cmd...

@ECHO OFF

SETLOCAL ENABLEDELAYEDEXPANSION

CALL :Attempt
IF !errorlevel! NEQ 0 (ECHO Attempt Failed) ELSE (ECHO Attempt succeeded!)
GOTO :eof

:Attempt
   SETLOCAL
   CALL somethingThatFails
   SET retcode=!errorlevel!
   CALL somethingThatPasses : don't care about the errorlevel here
   CALL :return %retcode%
   ENDLOCAL
   CALL :eof

:return
   ECHO @exit /b %1 >return.cmd
   CALL ret.bat
   GOTO :eof

somethingthatfails.cmd...

DIR some command that fails >nul 2>&1

somethingthatpasses.cmd...

DIR >nul 2>&1

Одним из побочных эффектов этого является файл, лежащий вокруг имени ret.cmd. Обычно я использую подпрограмму: end, которая выполняет очистку и удаляет ее.

Ответ 4

Это предназначено для выполнения элемента %% я только в том случае, если оно существует и выполняется с проверкой ошибок и перемещением или протоколированием. если элемент %% я не существует, он ничего не сделает.

REM Loop through each line of input.txt
FOR /F "tokens=1-3 delims=, " %%i IN (.\ready\input.txt) DO (
  ECHO.
  ECHO.
  ECHO.
  ECHO Check %%i exists, execute it if it does
  if exist .\ready\%%i (
    call .\ready\%%i
    ECHO Move %%i to archive if no error occured 
    if not errorlevel 1 (
        copy .\ready\%%i .\archive\%mydate%_%mytime%_%%j_%%k_%%i
      ) else (
        ECHO Copy line of text to the new output.txt file if an error occurred
        >>output.txt %%i, %%j, %%k
      )
  )
)