2010-07-12 3 views
14

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

  • установить некоторые флаги компиляции
  • беги «gmake все»
  • вызов функции «проверить уровень ошибки», и если Равен 1, выход

Так это выглядит следующим образом:

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

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

: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 ..» на каждом шагу?

+2

Вы не можете. Просто замените 'call: interactive_check' на' if errorlevel 1 goto error'. Существует не так много различий между копированием в прошлом или последним. :) – atzz

+0

Получил, решил проблему с этим, и он отлично работает, спасибо! – Gui13

ответ

16

Используя фатальную ошибку синтаксиса, как показывает Джеб, действительно убивает всю пакетную обработку, но он также имеет неприятный побочный эффект - изменения окружающей среды после SETLOCAL сохраняется, несмотря на то, что они должны быть отброшены с помощью неявной Endlocal при завершении пакетной обработки. См. Мою статью DosTips SETLOCAL continues after batch termination! для получения дополнительной информации.

Основываясь на информации на Why does a non-interactive batch script think I've pressed 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 

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

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

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

C:\test> 

The: Процедура 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, но вы также можете поймать исключение на более высоком уровне, исключить специальный код очистки исключений и возобновить обработку или продолжить выход через другой бросок! См. Does Windows batch support exception handling? для получения дополнительной информации.

+0

Изощренные. Рад, что вы смогли быстро открыть мое открытие для хорошей работы; почти компенсирует избиение, которое оно вызвало у меня изначально. :-) –

+0

Ну, ты заслужил прием, сэр. – Gui13

+0

Хорошее решение, для обычных случаев чище, чем мое. – jeb

20

Для хорошего решения на ту часть улучшенной версии

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

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

Cool solution :) Я решил проблему, используя предложение atzz, но в случае, если кто-то спотыкается здесь, вы будете ответом. – Gui13

+1

Это решение ненадлежащим образом сохранит изменения среды после SETLOCAL. Я обнаружил [чистое решение] (http://stackoverflow.com/a/25474648/1012053), которое правильно отменяет все изменения после SETLOCAL. – dbenham

1

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

@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's post), потому что он завершает также вызывающая текущий пакетный файл также.

1

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