Подтвердить что ты не робот

Выйти из пакета script изнутри функции

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

  • установить некоторые флаги компиляции
  • запустите '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.." на каждом шагу?

4b9b3361

Ответ 1

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

Основываясь на информации в Почему неинтерактивная партия script думает, что я нажал control-C?, я обнаружил чистый способ выйти из всех пакетный сценарий из подпрограммы CALLed или script, и все изменения после 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

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

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

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

C:\test>

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

@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

Для хорошего решения см. улучшенную версию части

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

Вам нужно только создать синтаксическую ошибку, например, с пустым блоком (), чтобы подавить сообщение об ошибке, оно может быть выполнено в вызове, а 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

Ответ 3

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

@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

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

Ответ 4

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

Выход из этого script:

myfunction: Everything is good.
myfunction: got a FAIL. Will exit.