Вихід із пакетного файлу з підпрограми


20

Як я можу вийти з пакетного файлу з підпрограми?

Якщо я використовую команду EXIT, я просто повертаюся до рядка, де я викликав підпрограму, і виконання триває.

Ось приклад:

@echo off
ECHO Quitting...
CALL :QUIT
ECHO Still here!
GOTO END

:QUIT
EXIT /B 1

:END
EXIT /B 0

Вихід:

Quitting...
Still here!

Оновлення:

Це не правильна відповідь, але я закінчила щось робити:

@echo off
CALL :SUBROUTINE_WITH_ERROR || GOTO HANDLE_FAIL
ECHO You shouldn't see this!
GOTO END

:SUBROUTINE_WITH_ERROR
ECHO Simulating failure...
EXIT /B 1

:HANDLE_FAIL
ECHO FAILURE!
EXIT /B 1

:END
ECHO NORMAL EXIT!
EXIT /B 0

Двотрубна заява:

CALL :SUBROUTINE_WITH_ERROR || GOTO HANDLE_FAIL

це скорочення для:

CALL :SUBROUTINE_WITH_ERROR 
IF ERRORLEVEL 1 GOTO HANDLE_FAIL    

Я все одно хотів би дізнатися, чи є спосіб вийти безпосередньо з підпрограми, а не змушувати КЛІНАТА вирішувати ситуацію, але це, принаймні, робить роботу.


Оновлення №2: Коли ви запускаєте підпрограму з іншої підпрограми, викликаної вищезгаданим способом, я закликаю зсередини підпрограм таким чином:

CALL :SUBROUTINE_WITH_ERROR || EXIT /B 1

Таким чином, помилка поширюється назад до "головної", так би мовити. Основна частина партії може потім обробляти помилку за допомогою обробника помилок GOTO: FAILURE

Відповіді:


21

Додайте це у верхню частину свого пакетного файлу:

@ECHO OFF
SETLOCAL

IF "%selfWrapped%"=="" (
  REM this is necessary so that we can use "exit" to terminate the batch file,
  REM and all subroutines, but not the original cmd.exe
  SET selfWrapped=true
  %ComSpec% /s /c ""%~0" %*"
  GOTO :EOF
)

Тоді ви можете просто зателефонувати:

  • EXIT [errorLevel] якщо ви хочете вийти з цілого файлу
  • EXIT /B [errorLevel] для виходу з поточної підпрограми
  • GOTO :EOF для виходу з поточної підпрограми

+1 за GOTO :EOF
фактичну

1
Дуже хороша. Я зробив невелику модифікацію, привласнити %~0змінної замість true: if not "%selfwrapped%"=="%~0" ( set selfwrapped=%~0 .... ). Таким чином, ви можете використовувати один і той же трюк у кількох пакетних сценаріях, які дзвонять один одному.
GolezTrol

Це чудове рішення. Як ви вважаєте, чи варто редагувати, щоб пояснити, як це працює? У мене знадобилася хвилина, щоб розпакувати все це і зрозуміти, що це насправді просто виклик пакетного файлу ( %~0) з усіма аргументами ( %*) з вкладеного cmd.exe, і /sвикористовується для управління способом %ComSpec%обробки аргументом подвійних лапок навколо дзвінок.
Шон

@Sean Я вважаю, стислість є більш корисною для більшості людей. Більше документів не вимагали протягом 7 років, відколи я написав її, тому, схоже, це не користується великим попитом. Я також думаю, що є певна цінність для того, щоб люди переглядали речі і не копіювали / фрагментували документи. Але можливо, якщо ще кілька людей запитають, я можу щось додати. Це також CW, щоб ви могли запропонувати також редагування
Мерлін Морган-Грем

3

Як щодо цього незначного коригування?

@echo off
ECHO Quitting...
CALL :QUIT
:: The QUIT subroutine might have set the error code so let's take a look.
IF ERRORLEVEL 1 GOTO :EOF
ECHO Still here!
GOTO END

:QUIT
EXIT /B 1

:END
EXIT /B 0

Вихід:

Quitting...

Технічно це не виходить з підпрограми. Скоріше, він просто перевіряє результат підпрограми і вживає звідти дії.


2
Дякую, це, безумовно, виконає роботу, і якщо я не можу знайти кращої відповіді, це те, що я повинен зробити. Однак мені не доведеться вставляти цей рядок після кожного CALL у моєму довгому та складному пакетному файлі.
Браун

1

Якщо ви не хочете повертатися з процедури, не використовуйте call: натомість використовуйте goto.

@echo off
ECHO Quitting...
GOTO :QUIT
ECHO Will never be there!
GOTO END

:QUIT
EXIT /B 1

:END
EXIT /B 0

Суть питання в тому, як це зробити з підпрограм (тобто, використовуючи виклик), щоб це не відповіло на нього.
Стів Крейн

1

Я поміщаю обробку помилок у мої пакетні файли. Ви можете викликати обробників помилок так:

CALL :WARNING "This is" "an important" "warning."

І ось кінець пакетного файлу:

::-------------------------------------------------------------------
::  Decisions
::-------------------------------------------------------------------
:INFO
IF "_DEBUG"=="true" (
  ECHO INFO: %~1
  IF NOT "%~2"=="" ECHO          %~2
  IF NOT "%~3"=="" ECHO          %~3
)
EXIT /B 0
:WARNING
ECHO WARNING: %~1
IF NOT "%~2"=="" ECHO          %~2
IF NOT "%~3"=="" ECHO          %~3
EXIT /B 0
:FAILURE
ECHO FAILURE: %~1
IF NOT "%~2"=="" ECHO          %~2
IF NOT "%~3"=="" ECHO          %~3
pause>nul
:END
ECHO Closing Server.bat script
FOR /l %%a in (5,-1,1) do (TITLE %TITLETEXT% -- closing in %%as&PING.exe -n 2 -w 1 127.0.0.1>nul)

1

Це вийде з поточного контексту та з батьківського контексту (тобто, коли буде виконано всередині одного callглибокого сценарію підпрограми, вийде):

(goto) 2>nul || exit /b

Або якщо вам потрібен рівень помилок 0:

(goto) 2>nul || (
    type nul>nul
    exit /b
)

В основному, (goto) 2>nulвстановлює рівень помилок на 1 (без виведення помилки), повертає виконання до батьківського контексту та коду після того, як подвійний конвеєр виконаний у батьківському контексті. type nul>nulвстановлює рівень помилок на 0.

UPD:

Щоб повернути виконання більше, ніж два рази поспіль, ланцюжком кілька (goto) 2>nul ||подібних:

(goto) 2>nul || (goto) 2>nul || (goto) 2>nul || (
    type nul>nul
    exit /b
)

Ось рекурсивна підпрограма для повернення контексту змінну кількість разів:

:Kill
(goto) 2>nul || (
    set /a depth=%1-1
    if %1 GEQ 1 (
        call:Kill !depth!
    )
    (goto) 2>nul || (type nul>nul)
)

При виклику з рекурсивної функції:

@echo off
setlocal EnableDelayedExpansion
call:Recurs 5
echo This won't be printed
exit /b

:Recurs
set /a ri+=1
echo %ri%
if %ri% LSS %1 (
    call:Recurs %1
)
echo This will be printed only once
call:Kill %1
exit /b

вихід буде:

1
2
3
4
5
This will be printed only once
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.