Salir del archivo por lotes de la subrutina

20

¿Cómo puedo salir de un archivo por lotes desde una subrutina?

Si uso el comando EXIT, simplemente regreso a la línea donde llamé a la subrutina y la ejecución continúa.

Aquí hay un ejemplo:

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

:QUIT
EXIT /B 1

:END
EXIT /B 0

Salida:

Quitting...
Still here!

Actualizar:

Esta no es una respuesta adecuada, pero terminé haciendo algo como:

@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

La declaración de doble tubo de:

CALL :SUBROUTINE_WITH_ERROR || GOTO HANDLE_FAIL

es la abreviatura de:

CALL :SUBROUTINE_WITH_ERROR 
IF ERRORLEVEL 1 GOTO HANDLE_FAIL    

Todavía me gustaría saber si hay una manera de salir directamente de una subrutina en lugar de tener que hacer que el LLAMADOR se encargue de la situación, pero esto al menos hace el trabajo.


Actualización n. ° 2: cuando llamo a una subrutina desde otra subrutina, llamada de la manera anterior, llamo desde dentro de las subrutinas por lo tanto:

CALL :SUBROUTINE_WITH_ERROR || EXIT /B 1

De esta manera, el error se propaga de nuevo al "principal", por así decirlo. La parte principal del lote puede manejar el error con el controlador de errores GOTO: FAILURE

Brown
fuente

Respuestas:

21

Agregue esto a la parte superior de su archivo por lotes:

@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
)

Entonces simplemente puede llamar:

  • EXIT [errorLevel] si quieres salir del archivo completo
  • EXIT /B [errorLevel] para salir de la subrutina actual
  • GOTO :EOF para salir de la subrutina actual
Merlyn Morgan-Graham
fuente
+1 por mencionarlo realmenteGOTO :EOF
afrazier
1
Muy agradable. He hecho una pequeña modificación, asignar %~0a la variable en lugar de true: if not "%selfwrapped%"=="%~0" ( set selfwrapped=%~0 .... ). De esa manera, puede usar el mismo truco en múltiples secuencias de comandos por lotes que se llaman entre sí.
GolezTrol
Esta es una gran solución. ¿Crees que vale la pena editar para explicar cómo funciona? Me tomó un minuto desempacar todo eso y darme cuenta de que en realidad solo está llamando al archivo por lotes ( %~0) con todos los argumentos ( %*) de un cmd.exe anidado, y /sse usa para controlar la forma en que el %ComSpec%argumento maneja las comillas dobles alrededor del llamada.
Sean
@Sean Creo que la brevedad es más útil para la mayoría de las personas. No se habían pedido más documentos en los 7 años desde que lo escribí, por lo que no parece tener una gran demanda. También creo que hay algo de valor para las personas que buscan cosas por sí mismas y no duplican / fragmentan documentos. Pero tal vez si algunas personas más preguntan, podría agregar algo. También es un CW, por lo que también puedes proponer una edición
Merlyn Morgan-Graham el
3

¿Qué tal este pequeño ajuste?

@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

Salida:

Quitting...

Técnicamente esto no sale de la subrutina. Por el contrario, simplemente verifica el resultado de la subrutina y toma medidas desde allí.

JMD
fuente
2
Gracias, eso ciertamente haría el trabajo, y si no puedo encontrar una mejor respuesta, eso es lo que tendré que hacer. Sin embargo, prefiero no tener que pegar esa línea después de cada LLAMADA en mi largo y complejo archivo por lotes.
Marrón
1

Si no desea regresar del procedimiento, no use call: en su lugar, use goto.

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

:QUIT
EXIT /B 1

:END
EXIT /B 0
dolmen
fuente
El punto de la pregunta es cómo hacerlo desde subrutinas (es decir, usando la llamada) para que esto no responda.
Steve Crane
1

Puse el manejo de errores en mis archivos por lotes. Puede llamar a los controladores de errores de esta manera:

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

Y aquí está el final del archivo por lotes:

::-------------------------------------------------------------------
::  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)
djangofan
fuente
1

Esto saldrá del contexto actual y de un contexto principal (es decir, cuando se ejecute dentro de una callsecuencia de comandos de subrutina profunda se cerrará):

(goto) 2>nul || exit /b

O, si necesita errorlevel 0:

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

Básicamente, (goto) 2>nulestablece el nivel de error en 1 (sin generar un error), devuelve la ejecución al contexto primario y al código después de que se ejecuta la doble canalización en el contexto primario. type nul>nulestablece errorlevel en 0.

UPD:

Para devolver la ejecución más de dos veces seguidas, encadene varias de (goto) 2>nul ||esta manera:

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

Aquí hay una subrutina recursiva para devolver el contexto un número variable de veces:

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

Cuando se llama desde una función recursiva:

@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

la salida será:

1
2
3
4
5
This will be printed only once
revs SER HÍBRIDO
fuente