Most solutions only work for 2 stack levels or with on-screen error messages.
So I came up with two solutions and I think both are better than what I've seen around here.
SOLUTION FOR COMPLEX SCRIPTS WITHOUT SETLOCAL AND WITHOUT ERRORLEVEL
This solution applies when:
- you have too many recursive or conditional functions not knowing exactly when the program will terminate
- you don't care about variables that stay in the environment
- you don't care about errorLevel
CALL :leave 2>nul
:leave
()
GOTO :eof
Explanation: It will call :leave
putting it at the stack level, and pseudo-parameter 2>nul
will send error messages to void, suppressing errors from that block of code. And then ()
will generate a fatal error that will kill the entire program.
You can even remove your variables by setting SET blank or putting ENDLOCAL in the main before CALL :leave
.
But in a complex program you wouldn't know when to do this. But in this case, I came up with a better solution...
I know many have seen this code around here: (GOTO) 2>nul & ENDLOCAL & EXIT /b %yourCode%
, but like I said, it just kills the current function and terminates the previous function that called it. It doesn't work if you have multiple functions on the stack calling each other. So you could force several errors:
(GOTO) 2>nul & (GOTO) 2>nul & (GOTO) 2>nul & ENDLOCAL & EXIT /b
However, you don't know how many times to do this, and if you overdo it, you can even kill the :main and execute an exit /b that will close the console. Then I have the
SOLUTION FOR COMPLEX SCRIPTS WITH SETLOCAL AND WITH ERRORLEVEL
This solution applies when:
- you have too many recursive or conditional functions not knowing exactly when the program will terminate
- you don't want remnant variables
- you check errorLevel (optional)
SET dummy=iAmDefined
FOR /l %%i in (0, 1, 1000) do (
IF defined dummy (
(GOTO) 2>nul & IF NOT defined dummy (CMD /c EXIT /b %yourExitCode%)
)
)
It won't run a thousand times, but only as many times as necessary until !dummy! miss the assignment.
You don't need to enable EnableDelayedExpansion unless you want to debug the !dummy! echo.
Example:
@ECHO off
SETLOCAL EnableDelayedExpansion
SET dummy=iAmDefined
CALL :func3
:func1
ECHO i'm in func1
CALL :leave 2>nul
::GOTO :eof
:func2
ECHO i'm in func2
CALL: func1
::GOTO :eof
:func3
ECHO i'm in func3
CALL: func2
::GOTO :eof
:leave
ECHO ===leaving===
FOR /l %%i in (1, 1, 1000) of (
IF defined dummy (
(GOTO) & ECHO %%i !dummy! & IF NOT defined dummy (CMD /c EXIT /b 555)
)
)
::GOTO :eof
::ENDLOCAL
Exit e Explanation:
-- 1 call (called func3)
i'm in func3 -- 2 calls (called func2)
i'm in func2 -- 3 calls (called func1)
i'm in func1 -- 4 calls (called leave)
===leaving===
1 iAmDefined -- rollback :leave
2 iAmDefined -- rollback :func3
3 iAmDefined -- rollback :func2
4 iAmDefined -- rollback :func1
5 !dummy! -- rollback :main before unset dummy
new CMD and exit after unset dummy
At the 5 dummy it was still set before the (GOTO), but the ECHO was after.
The :EOF are commented to ilustrate that these gotoes are not used
call :interactive_check
withif errorlevel 1 goto error
. There is not much difference between copy-pasting former or latter. :) – atzz