8

I have 4 batch files, suppose a.bat, b.bat, c.bat and d.bat. Now these batch files are called in such a manner that a.bat calls b.bat, b.bat calls c.call and so on.

If I get any error in any batch file, I want to exit from the entire program by saying an error occurred, and mention which batch file had a problem. My question is, how can I do this?

Here I used exit /b but it only gets out from the current batch file and moves back into batch file from whence it was called:

a.bat

@echo. off
echo. this is batch 'a'
call b.bat

b.bat

@echo. off
echo. this is batch 'b'
call c.bat

c.bat

@echo. off
echo. this is batch 'c'

I get an error in batch 'C' - It should then report an error and exit, but it's moving back into batch 'B' somehow. Any idea on how to exit from a nested batch file?

Community
  • 1
  • 1
user2583646
  • 81
  • 1
  • 2

5 Answers5

5

You can use a syntax error, this stop immediately the batch without closing the command window.

The :HALT functions calls the :__halt function only to supress the error message.

c.bat

@echo off
echo this is batch 'c'
echo An error occurs
call :HALT
exit /b

:HALT
call :__halt 2> nul
exit /b

:__halt
()
jeb
  • 78,592
  • 17
  • 171
  • 225
  • You can do it without additional `:HALT`, works here in XP without error msg. I mean directly call `:__halt 2>nul`. – Endoro Jul 15 '13 at 13:51
  • 1
    @Endoro That's obviously, but I want to provide a `HALT` function to call without any extra text – jeb Jul 15 '13 at 14:15
  • +1 for a clever solution. However, I believe that if you know you are at a failure point that the script should exit gracefully providing meaningful output as it goes. – David Ruhmann Jul 15 '13 at 20:57
  • 1
    This clever technique has an unfortunate side effect - SETLOCAL are not terminated properly after a fatal syntax error. See [my answer](http://stackoverflow.com/a/25475104/1012053) for another method that more cleanly terminates batch processing. – dbenham Aug 24 '14 at 18:49
2

You might try this (c.bat):

@echo. this is batch 'c'
@pause
exit
Endoro
  • 37,015
  • 8
  • 50
  • 63
  • 1
    Yes, but the OP can call the batch `cmd /c a.bat`. This will not close the window. But yours is fine. – Endoro Jul 15 '13 at 13:48
2

You can use errorlevels with exit codes, as described here: http://www.robvanderwoude.com/errorlevel.php

In particular, if you want to do a manual error, then c.bat or b.bat should explicitly have an exit code specified with

EXIT /b 1

(or a number of your choosing), but if you just want windows automatic errors to count, then right after running b.bat or c.bat, you can just write

IF ERRORLEVEL 1 EXIT /b %ERRORLEVEL%

Which will propagate the same error up to the next program, so they can exit immediately if you so wish. Has the advantage that you can stop propagating upwards whenever you want.

(edit: to be clear, the second line of code mentioned here is necessary in all but the bottom-level program whether you're using manual or automatic errors)

codetaku
  • 498
  • 3
  • 13
  • This will not break the chain. – Endoro Jul 15 '13 at 13:57
  • I'm afraid I don't understand the use case for what you and jeb think he's asking for, therefore, I'm giving him the answer to what /I/ think he's asking for, which is "I want b.bat to stop running the rest of the code I specified in it after c.bat errors, and I want a.bat to do the same for b.bat". If I'm wrong about what he meant to ask, he'll obviously accept one of your answers instead of mine. – codetaku Jul 15 '13 at 14:00
  • From the question: `Here I used exit /b but it only gets out from the current batch file and moves back into batch file from whence it was called`. I have no insight in reasonable use. This might be, but is beyond my scope. Moderation is not my department. – Endoro Jul 15 '13 at 14:13
2

jeb's fatal syntax error method works, but it does not properly terminate any active SETLOCAL.

For example, here is fatalTest.bat:

@echo off
setlocal enableDelayedExpansion
set test=AFTER main SETLOCAL
call :sub
echo returning from main  NEVER REACHED
exit /b

:sub
setlocal
set test=AFTER sub SETLOCAL
set test
call :ExitBatch
echo returning from sub2 NEVER REACHED
exit /b


:ExitBatch
call :__exitBatch 2>nul
:__ExitBatch
for

And here is sample run output demonstrating how test is still defined and delayed expansion is still enabled after batch processing terminates:

C:\test>fatalTest
test=AFTER sub SETLOCAL

C:\test>echo !test!
AFTER sub SETLOCAL

C:\test>

It is now impossible to restore the environment to a pre-batch state. The CMD.EXE session should be terminated.


Here is an improved CtrlCTest.bat version that properly terminates any active SETLOCAL upon batch termination. It uses a surprising technique to programmatically "press" Ctrl-C that I learned about at Why does a non-interactive batch script think I've pressed control-C?

@echo off
setlocal enableDelayedExpansion
set test=AFTER main SETLOCAL
call :sub
echo returning from main  NEVER REACHED
exit /b

:sub
setlocal
set test=AFTER sub SETLOCAL
set test
call :ExitBatch
echo returning from sub2 NEVER REACHED
exit /b


:ExitBatch - Cleanly exit batch processing, regardless how many CALLs
if not exist "%temp%\ExitBatchYes.txt" call :buildYes
call :CtrlC <"%temp%\ExitBatchYes.txt" 1>nul 2>&1
:CtrlC
cmd /c exit -1073741510

:buildYes - Establish a Yes file for the language used by the OS
pushd "%temp%"
set "yes="
copy nul ExitBatchYes.txt >nul
for /f "delims=(/ tokens=2" %%Y in (
  '"copy /-y nul ExitBatchYes.txt <nul"'
) do if not defined yes set "yes=%%Y"
echo %yes%>ExitBatchYes.txt
popd
exit /b

And here is sample run output. You can see that delayed expansion is no longer active, and test is no longer defined:

C:\test>CtrlCTest
test=AFTER sub SETLOCAL

C:\test>echo !test!
!test!

C:\test>set test
Environment variable test not defined

C:\test>

You could put the :ExitBatch routine in a stand-alone ExitBatch.bat script that resides somewhere within your PATH, and then any batch can conveniently CALL the utility as needed.

@echo off
:ExitBatch - Cleanly exit batch processing, regardless how many CALLs
if not exist "%temp%\ExitBatchYes.txt" call :buildYes
call :CtrlC <"%temp%\ExitBatchYes.txt" 1>nul 2>&1
:CtrlC
cmd /c exit -1073741510

:buildYes - Establish a Yes file for the language used by the OS
pushd "%temp%"
set "yes="
copy nul ExitBatchYes.txt >nul
for /f "delims=(/ tokens=2" %%Y in (
  '"copy /-y nul ExitBatchYes.txt <nul"'
) do if not defined yes set "yes=%%Y"
echo %yes%>ExitBatchYes.txt
popd
exit /b
Community
  • 1
  • 1
dbenham
  • 127,446
  • 28
  • 251
  • 390
0

If the .BAT file is under TakeCommand, use CANCEL.

ClioCJS
  • 64
  • 3
  • 11