Выход из пакетного скрипта из функции - PullRequest
17 голосов
/ 12 июля 2010

У меня проблема с моим командным файлом.Он автоматически создает несколько программ, выполнив что-то вроде этого:

  • установить некоторые флаги компиляции
  • запустить 'gmake all'
  • , вызвать функцию "проверить уровень ошибок" иесли уровень ошибки 1, выйдите из

Так это выглядит так:

set FLAG=1
...
gmake all
call :interactive_check
set OTHERFLAG=1
...
gmake all
call :interactive_check

Есть 6 или 7 из них (и они могут расти).Поэтому я сделал функцию проверки уровня ошибки вместо копирования / вставки на каждом этапе.Проблема заключается в следующем: проверка ошибок производится с помощью функции:

:interactive_check
if errorlevel 1 (
echo.
echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\
echo Error in compilation process... exiting
echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\
echo.
cd %root_dir%
exit /B 1
) ELSE (
echo.Continuing to next step
)
goto:eof

Теперь при запуске exit /B 1 просто выходит из функции, , но не в пакетном файле .

Знаете ли вы, как выйти из полного пакетного файла без необходимости копировать / вставлять мое "if errorlevel 1 .." на каждом шаге?

Ответы [ 4 ]

22 голосов
/ 19 марта 2011

Хорошее решение см. В улучшенной версии детали

Вы можете остановить пакет в любой точке, в том числе внутри вызовов вложенных функций.

Вам нужно только создать синтаксическую ошибку, например, с пустым блоком (), чтобы подавить сообщение об ошибке, оно может быть выполнено во время вызова, и stderr вызова перенаправляется на nul.

@echo off
setlocal enabledelayedexpansion

rem Do something
call :interactive_check

rem Do something
call :interactive_check

goto :eof

::::::::::::::::::::::::::
:interactive_check
if errorlevel 1 (
    echo.
    echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\
    echo Error in compilation process... exiting
    echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\
    call :halt 1
) ELSE (
    echo.Continuing to next step
)
goto :eof

:: Sets the errorlevel and stops the batch immediately
:halt
call :__SetErrorLevel %1
call :__ErrorExit 2> nul
goto :eof

:__ErrorExit
rem Creates a syntax error, stops immediately
() 
goto :eof

:__SetErrorLevel
exit /b %time:~-2%
goto :eof

2017-04-09 Улучшенная версия: выход только из текущей партии, но не из партии вызывающей стороны

Как уже упоминалось @dbenham, есть новая техникадля обработки исключений, которая также может использоваться для выхода только из текущего пакета.

@echo off
echo Do something, detecting some fatal error
call :ExitBatch 3
exit /b

:ExitBatch [errorcode] - Exits only the current batch file, regardless how many CALLs
set _errLevel=%1
REM *** Remove all calls from the current batch from the call stack
:popStack
(
    (goto) 2>nul
    setlocal DisableDelayedExpansion    
    call set "caller=%%~0"
    call set _caller=%%caller:~0,1%%
    call set _caller=%%_caller::=%%
    if not defined _caller (
        REM callType = func
        rem set _errLevel=%_errLevel%
        goto :popStack
    )   
    (goto) 2>nul
    endlocal
    cmd /c "exit /b %_errLevel%"
)
echo NEVER REACHED
exit /b
19 голосов
/ 24 августа 2014

Использование фатальной синтаксической ошибки, как демонстрирует jeb, убивает всю пакетную обработку, но также имеет неприятный побочный эффект - изменения среды после SETLOCAL сохраняются, даже если предполагается, что они будут отброшены через неявный ENDLOCAL, когда пакетная обработка завершится,См. Мой пост DosTips SETLOCAL продолжается после завершения пакета! для получения дополнительной информации.

Основано на информации в Почему неинтерактивный пакетный скрипт считает, что я нажал control-C?, я обнаружил чистый способ выхода из всех пакетных сценариев из подпрограммы или сценария CALLed, и все изменения после SETLOCAL должным образом отбрасываются.

@echo off
setlocal
set test=AFTER main SETLOCAL
call :sub
echo returning from main  NEVER REACHED
exit /b

:sub
setlocal
set test=AFTER sub SETLOCAL
set test
call :ExitBatch
echo returning from sub2 NEVER REACHED
exit /b


:ExitBatch - Cleanly exit batch processing, regardless how many CALLs
if not exist "%temp%\ExitBatchYes.txt" call :buildYes
call :CtrlC <"%temp%\ExitBatchYes.txt" 1>nul 2>&1
:CtrlC
cmd /c exit -1073741510

:buildYes - Establish a Yes file for the language used by the OS
pushd "%temp%"
set "yes="
copy nul ExitBatchYes.txt >nul
for /f "delims=(/ tokens=2" %%Y in (
  '"copy /-y nul ExitBatchYes.txt <nul"'
) do if not defined yes set "yes=%%Y"
echo %yes%>ExitBatchYes.txt
popd
exit /b

Вот пример выходных данных запуска вышеуказанного теста.летучая мышь.Вы можете видеть, что скрипт никогда не возвращался из вызова: ExitBatch, и определение тестовой переменной было правильно отброшено после завершения пакетной обработки.

C:\test>test.bat
test=AFTER sub SETLOCAL

C:\test>set test
Environment variable test not defined

C:\test>

Процедура: ExitBatch может быть помещена в ее собственный файл ExitBatch.batсценария и помещен где-то в пределах вашей переменной PATH, так что он может быть удобно использован любым пакетным сценарием.

@echo off
:ExitBatch - Cleanly exit batch processing, regardless how many CALLs
if not exist "%temp%\ExitBatchYes.txt" call :buildYes
call :CtrlC <"%temp%\ExitBatchYes.txt" 1>nul 2>&1
:CtrlC
cmd /c exit -1073741510

:buildYes - Establish a Yes file for the language used by the OS
pushd "%temp%"
set "yes="
copy nul ExitBatchYes.txt >nul
for /f "delims=(/ tokens=2" %%Y in (
  '"copy /-y nul ExitBatchYes.txt <nul"'
) do if not defined yes set "yes=%%Y"
echo %yes%>ExitBatchYes.txt
popd
exit /b

Важное обновление:

Теперь можно реализовать надежныйобработка исключений с чистой партией.Вы можете не только выбросить исключение, которое может прекратить всю пакетную обработку с любого уровня CALL, но вы также можете перехватить исключение на более высоком уровне, исключить специальный код обработки для обработки исключений и возобновить обработку, или продолжить выход через другой выброс!См. Поддерживает ли пакетная обработка Windows обработку исключений? для получения дополнительной информации.

2 голосов
/ 06 апреля 2017

Когда вы используете exit /b X для выхода из функции, для ERRORLEVEL устанавливается значение X. Затем вы можете использовать символ условной обработки || для выполнения другой команды, если ERRORLEVEL отличен от нуля после call.

@echo off
setlocal
call :myfunction PASS || goto :eof
call :myfunction FAIL || goto :eof
echo Execution never gets here
goto :eof

:myfunction
    if "%1"=="FAIL" ( 
        echo myfunction: got a FAIL. Will exit.
        exit /b 1
    )
    echo myfunction: Everything is good.
    exit /b 0

Вывод этого скрипта:

myfunction: Everything is good.
myfunction: got a FAIL. Will exit.
1 голос
/ 31 августа 2015

Я предлагаю следующее решение:

@echo off

:: comment next line if you want to export any local variables in caller environment
setlocal

set FLAG=1
rem Do something
call :interactive_check

set FLAG2=2
rem Do something
call :interactive_check

goto :eof

:interactive_check
if errorlevel 1 (
    echo.
    echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\
    echo Error in compilation process... exiting
    echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\
    (goto) 2>nul & endlocal & exit /b %ERRORLEVEL%
) else (
    echo Continuing to next step
)
goto :eof

Сохраняет код ошибки из команды, которая выдает ошибку.Если вы хотите сохранить код ошибки в определенное значение, измените строку:

(goto) 2>nul & endlocal & exit /b YOUR_EXITCODE_HERE

Мне не нравится решение с синтаксической ошибкой (см. сообщение Джеба ), потому что оно также заканчиваетсявызывающий текущий пакетный файл также.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...