'Subroutines in batch files

Given the following code:

@Echo off
ECHO Start
ECHO Calling SUB_A
CALL :SUB_A
ECHO Calling SUB_B
CALL :SUB_B

:SUB_A
    ECHO In SUB_A
    GOTO:EOF

:SUB_B
    ECHO In SUB_B
    GOTO:EOF

ECHO End

I expect this output:

Start
Calling SUB_A
In SUB_A
Calling SUB_B
In SUB_B
End

But I get this:

Start
Calling SUB_A
In SUB_A
Calling SUB_B
In SUB_B
In SUB_A

What am I doing wrong here?



Solution 1:[1]

If you want to return from a CALL, you use EXIT command with /B argument (as "EXIT" alone will terminate the batch file).

For example:

CALL :SUB_ONE
CALL :SUB_TWO

GOTO :EOF

:SUB_ONE
ECHO Hello from one
EXIT /B

:SUB_TWO
ECHO Hello from two
EXIT /B

:EOF

Solution 2:[2]

After your line CALL :SUB_B the batch file falls through to SUB_A. If you don't want it to, you need to put a GOTO line there.

Solution 3:[3]

Just to stick my $0.02 worth in here:

I would have to say that DOS files (or DOS batch files) should always be written as :

[ Main Program ]

EXIT

[ All Subroutines ]

In this way - you can never fall into the subroutine section.

Also - always use REM statements before, during, and possibly after each section so you always know what a section is going to do.

And last - but not least - See if you can write a batch file so it becomes more generic so you can set it to one side and use it when you need to again.

Just my $0.02 worth of common sense you probably already know. :-)

OH! Sorry! Forgot to post my small sleeper subroutine I wrote. Here it is! :-)

REM
REM Calling the Sleeper subroutine
REM
call :sleeper
exit

REM
REM Sleeper subroutine
REM
REM qna = Questions and Answers
REM
REM Interestingly - you can NOT use a '>'
REM Because DOS uses it for redirection!
REM Instead - use a question mark (?)
REM
:sleeper
set /p qna=Press return to continue or 'q' to stop ?
if [%qna%] == [q] (
    echo Exiting....please wait.
    exit
    )

goto :eof

Solution 4:[4]

Do not use EXIT!

Example:

@rem a.bat
@echo off
echo a.bat
timeout /t 1

call b.bat

call c.bat
@rem b.bat
@echo off
echo b.bat
timeout /t 1

call :subB
rem call to c.bat will fail if using "exit" instead of "goto :end"
goto :end

:subB
echo subB
timeout /t 1
exit /b

:end
@rem c.bat
@echo off
echo c.bat
timeout /t 1

"exit" immediately aborts the call chain!

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 aikeru
Solution 2 BoltBait
Solution 3
Solution 4 Joe