4

I understand if you have two .bat or .cmd files, let's call them foo and bar, the following rules apply:

Without call:

:: Welcome to foo.bat
@bar.bat
@echo We never get to this line because bar.bat is "chain-executed".

With call:

:: Welcome to foo.bat
@call bar.bat
@echo This line is executed after bar.bat returns.

My question is: is there a way of performing the converse operation, i.e. ensuring that a non-batch-file executable is chained?

:: Welcome to foo.bat
@chain bar.exe
@echo Even though bar is now an .exe,  we never get to this line.
@echo At least, that would be the case if the "chain" command really existed.

In other words, is there a way of performing the function of the fictitious chain command in that last example?

jez
  • 14,867
  • 5
  • 37
  • 64
  • Just put bar.exe on a line and it will execute. When the program exits, it will return to the batch file. – Squashman Dec 11 '15 at 17:41
  • @Squashman The disadvantage of that would be that `foo.bat`, when double-clicked from Windows explorer, leaves its shell window hanging around while waiting for `bar.exe` to complete. – jez Dec 11 '15 at 17:47
  • 2
    Then use the START command. – Squashman Dec 11 '15 at 17:48
  • Hmm. Actually my comment doesn't make sense: that behaviour also occurs when `bar` is a batch file. The choice of whether to use `start` or not is I suppose orthogonal to the issue of chaining, and I guess (??) there's no disadvantage to letting execution fall back into `foo` either after `start bar` or just `bar` – jez Dec 11 '15 at 17:55

1 Answers1

3

It is necessary to use command start to run the executable in a separate process and additionally exit current batch processing or entire command process.

@echo off
echo Welcome to %~nx0
start "Title" bar.exe & exit /B
echo Even though bar is now an .exe, we never get to this line.

This batch file starts bar.exe in a separate process with Title as window title in case of executable is a console application for the new console window opened in this case.

Then after start finished exit /B is unconditionally executed by command processor while bar.exe is running in a separate process resulting in terminating processing of current batch file.

If this batch file was not called itself from another batch file using command call, the command processor finishes now processing the batch file resulting in exiting command processing, except the batch file was called with cmd.exe with option /K to keep command prompt window open after finishing batch processing which is not the case by default.

But if this batch file was called with call from another batch file, just processing of this child batch file is finished and command processor continues processing of parent batch file while bar.exe runs in a separate process.

@echo off
echo Welcome to %~nx0
start "Title" bar.exe & exit
echo Even though bar is now an .exe, we never get to this line.

In this batch code the command exit is without option /B which results in terminating command processing after start finished starting bar.exe in a separate process even if the current batch file was called from another batch file with call and even if batch file processing was started with cmd.exe with parameter /K.

Instead of unconditionally concatenating the two commands start and exit with operator & it is also possible to use a block as shown below for the two variants.

With just exiting current batch processing:

@echo off
echo Welcome to %~nx0
(
    start "Title" bar.exe
    exit /B
)
echo Even though bar is now an .exe, we never get to this line.

With exiting entire command process:

@echo off
echo Welcome to %~nx0
(
    start "Title" bar.exe
    exit
)
echo Even though bar is now an .exe, we never get to this line.

Such a start of an application with exiting either current batch processing or entire command processing makes of course only sense when bar.exe is started depending on at least one condition in the batch file.

Note 1:
It is also possible to use goto :EOF instead of exit /B to end current batch processing.

Note 2: goto :EOF and exit /B result both in just exiting the subroutine if the command is being part of a batch subroutine, i.e. code below a label called with call :label because a batch subroutine is just like a child batch file embedded within a main batch file regarding batch processing.

Some more examples to demonstrate the behavior of call and exit /B:

Test1.bat:

@echo off
echo Running %~nx0
call Test2.bat
echo Finished %~nx0

Test2.bat:

@echo off
echo Running %~nx0
Test3.bat
echo Finished %~nx0

Test3.bat:

@echo off
echo Finished %~nx0

Running Test1.bat from within a command prompt window results in output:

Running Test1.bat
Running Test2.bat
Finished Test3.bat
Finished Test1.bat

So the line Finished Test2.bat is missing because command processor returned from Test3.bat directly to Test1.bat.

Next we compile following C code to console application Test.exe:

#include <stdio.h>

int main (int argc, char* argv[])
{
    if(argc > 1)
    {
        printf("Running %s with argument %s\n",argv[0],argv[1]);
    }
    else
    {
        printf("Running %s without an argument\n",argv[0]);
    }
    return 0;
}

And we make use of Test.exe in following 2 batch files:

Test4.bat:

@echo off
echo Running %~nx0
Test.exe 4
call Test5.bat
echo Finished %~nx0

Test5.bat:

@echo off
echo Running %~nx0
Test.exe 5
Test.exe 6 & exit /B
echo Finished %~nx0

Running Test4.bat from within a command prompt window results in output:

Running Test4.bat
Running Test.exe with argument 4
Running Test5.bat
Running Test.exe with argument 5
Running Test.exe with argument 6
Finished Test4.bat

So the line Finished Test5.bat is missing because command processor returned from executing Test.exe with argument 6 directly to Test4.bat.

But with using bar & exit /B it is nevertheless important if bar is a batch file with file extension bat or cmd, or an executable with file extension exe or com. This can be demonstrated by changing code of Test2.bat to:

@echo off
echo Running %~nx0
Test3.bat & exit /B
echo Finished %~nx0

Running Test1.bat from within a command prompt window results in output:

Running Test1.bat
Running Test2.bat
Finished Test3.bat

So with exit /B appended in second batch file, command processor interprets exit in second batch file as exit in context of first batch file.

Mofi
  • 46,139
  • 17
  • 80
  • 143
  • Nice, that taught me something I didn't know regarding `exit` vs `exit /B`. I guess the behaviour I was looking for is closest to `start bar & exit /B` for non-console processes and `bar & exit /B` for console processes. The latter has the same apparent behaviour whether `bar` is really `bar.bat` or `bar.exe` (although, under the hood, I think the order of operations differs slightly between the two - hopefully without any gotchas?) – jez Dec 11 '15 at 20:21
  • 1
    On using `bar & exit /B` it still matters if `bar` is a batch file or an executable depending on current batch file is just a child batch or a main batch. If `bar & exit /B` is within a batch file not called from a parent batch file with __call__, the processing is the same. But in case of this line is within a __called__ child batch file, the processing is different, see answer above with additional code examples. – Mofi Dec 11 '15 at 21:27
  • I see, that is a gotcha. But I guess `bar & goto :eof` doesn't have that problem? – jez Dec 12 '15 at 01:26
  • 1
    There is absolutely no difference in batch file processing between `bar & exit /B` and `bar & goto :EOF`. This can be seen by using instead of `exit /B` in above batch files `goto :EOF`. The output (= processing) is always the same. – Mofi Dec 12 '15 at 10:28
  • I was unable to replicate your assertion that "with `exit /B` appended in second batch file, command processor interprets exit in second batch file as exit in context of first batch file." I had two files: in `one.cmd` I have `@echo hello 1\n@call two\n@echo goodbye 1` and in `two.cmd` I have `@echo hello 2\n@exit /B\n@echo goodbye 2`. Even though `two` is invoked via `call, the `exit` inside it does not cause `cmd` to exit in the context of `one.cmd` - i.e. I still see "goodbye 1" in the console output. – jez Dec 16 '15 at 00:33
  • Yes, in your example with `one.cmd` __calls__ `two.cmd` the command `exit /B` in `two.cmd` results in just exiting processing of `two.cmd`. But in my example `Test1.bat` __calls__ `Test2.bat` containing the line `Test3.bat & exit /B` __without call__ and therefore continues execution on `Test3.bat`. The `exit /B` in `Test2.bat` is executed after `Test3.bat` finished, but already in context of `Test1.bat` and therefore `Finished Test1.bat` is not output. But `exit /B` in `Test2.bat` would just exit processing of `Test2.bat` if `Test3` would be an EXE instead of a BAT. – Mofi Dec 16 '15 at 12:16
  • Oh wow. I hadn't quite grasped that one batch file would chain another but *still* execute the second half of the line, after `&`. Thanks for clarification. – jez Dec 16 '15 at 19:03