Your assumption is wrong that execution returns from file two.bat
, because that is only the case when you are using the call
command1.
The batch file one.bat
runs two.bat
within a parenthesised block of code, which is already in a command stack2, so the block is (kind of) finished before terminating execution.
Your output perfectly illustrates what happens (therefore I commented it here):
J:\>one
1 2 /* first loop iteration, the first `echo %%i !j!` in the block is
executed as expected; */
Hello World /* `two.bat` is run; execution does NOT return to `one.bat`,
because there is NO `call`, which would put a return point onto
the stack! the only thing remembered and thus accomplished is
the current command line or parenthesised block; */
1 !j! /* the second `echo %%i !j!` in the block is executed as it is
still buffered, but `two.bat` is already quit, hence implicit
`endlocal` commands have been executed, so all the nested
`setlocal` commands in your scripts are cancelled and delayed
expansion is disabled (default state); moreover, variable `j` is
no longer defined here; */
/* at this point, the parenthesised block, which is the loop body,
has been executed, hence batch file context is left and Command
Prompt context applies, so `@echo off` from `one.bat` does no
longer apply here; */
J:\>( // this block is nothing but the echo of the second loop iteration,
set j=2 // which is still buffered;
set /A j=!j! + 1
echo 2 !j!
two.bat
echo 2 !j!
)
Missing operator. /* this error message is caused by the attempt to execute
`set /A j=!j! + 1` (remember that delayed expansion is no longer
enabled and `j` is undefined); */
2 !j! // first `echo %%i !j!` in the buffered second loop iteration;
Hello World /* `two.bat` is run again; afterwards, batch file context is left
again and Command Prompt context applies; */
2 !j! // second `echo %%i !j!` in the buffered second loop iteration;
To prove whether execution happens under batch file or Command Prompt context, just place set /A j
in the loop body in one.bat
as the last command, so you will get an additional output 0
after the output 1 !j!
and the second 2 !j!
, because set /A
does not return anything in batch file context, but it outputs the (last) result (without a trailing line-break) in Command Prompt context; the value of 0
shows that j
is no longer set.
1) There are a few exceptions: the called batch file is involved in a pipe, or it is run and parsed by a for /F
loop, because the batch file runs in a new cmd.exe
instance in such cases.
2) The same would be true if the called batch file was involved in a line with concatenated commands, hence something like two.bat & echo Fine
would echo Fine
upon execution of two.bat
.