I am trying to figure out how file1.bat can call file2.bat at a specified label.
I figured I can do it like this:
File1.bat
:config
There are several methods or tricks that allows to do this. In this solution the trick consists in change the context of the running program to the second Batch file. After this change, all labels in the second Batch file becomes local labels of the running program, so they may be directly called via the usual call :label
command that may also include arguments in an entirely standard way. The context of the running program must be recovered before the program terminates.
The way to change the context consist is just rename the second Batch file with the same name of the first one (and rename the first one to any other name) so when the Batch file processor looks for a label, it really search the contents of the second file! The files must be renamed back to their original names before the program terminate. The key point that makes this method work is that both sets of rename commands (and all code between them) must be enclosed in parentheses; in this way, the code is first parsed and then executed from memory, so the rename of the files on disk don't affect it. Of course, this point means that the access to the values of all variables in the code must be done via delayed !expansion! (or using the call %%variable%%
trick).
First.bat
@echo off
rem Change the running context to the second/library batch file:
(
ren first.bat temporary.bat
ren second.bat first.bat
rem From this point on, you may call any label in second.bat like if it was a local label
call :label2
echo Returned from :label2
call :label1
echo Returned from :label1
rem Recover the running context to the original batch file
ren first.bat second.bat
ren temporary.bat first.bat
)
echo This is first.bat ending...
pause > nul
Second.bat
@echo off
:label1
echo I am label1 subroutine at second.bat
goto :EOF
:label2
echo This is the second subroutine at second.bat
goto :EOF
OUTPUT
This is the second subroutine at second.bat
Returned from :label2
I am label1 subroutine at second.bat
Returned from :label1
This is first.bat ending...
You should note that this method allows to develop the subroutines in the first file in the usual way (with no context change) and then just move the completed subroutines to the second/library file, because the way to call them is exactly the same in both cases. This method does not require any change in the second/library file nor in the way the subroutines are called; it just needs to insert the two small blocks of rename commands in the first file.
EDIT: Restoring files when a run-time error happen
As indicated in a comment, if a run-time error happen after the files were renamed, the files are not renamed back to their original names; however, it is very easy to detect this situation by the presence of the second.bat file and restore the files if such a file does not exists. This test may be done in a supervisor section that run the original code in a separate cmd.exe context, so this section will always complete correctly even if the original code was cancelled by an error.
First.bat
@echo off
if "%~1" equ "Original" goto Original
rem This supervisor section run the original code in a separate cmd.exe context
rem and restore the files if an error happened in the original code
(
cmd /C "%~F0" Original
if not exist second.bat (
echo Error detected! Restoring files
ren first.bat second.bat & ren temporary.bat first.bat
)
goto :EOF
)
:Original
( ren first.bat temporary.bat & ren second.bat first.bat
call :label1
echo Returned from :label1
call :label2
echo Returned from :label2
ren first.bat second.bat & ren temporary.bat first.bat )
echo This is first.bat ending...
Second.bat
@echo off
:label1
echo I am label1 subroutine at second.bat
exit /B
:label2
set "var="
if %var% equ X echo This line cause a run-time error!
exit /B
OUTPUT
I am label1 subroutine at second.bat
Returned from :label1
No se esperaba X en este momento.
Error detected! Restoring files