Exiting "call file.bat" in an elegant way? Propose a better solution!

Microsoft Windows
Post Reply
User avatar
MigrationUser
Posts: 336
Joined: 2021-Jul-12, 1:37 pm
Contact:

Exiting "call file.bat" in an elegant way? Propose a better solution!

Post by MigrationUser »

18 Jan 2011 16:19
Tanel

I came across an issue that could be put in the "requested feature is lacking"-category. I also thought of a workaround for it. But to be honest, the workaround is not elegant and makes the code even bulkier. To get an understanding of the problem you should look at the output data. The issue, in short, is that there should be a way to - on request - exit the called batch file and return control to where it was called from. Using "goto :EOF" will not always work and "exit" will kill your cmd.exe-process. I know this is the place to ask when it comes to more in-depth questions. So if there is anyone out there who has experience with this specific issue I would be very thankful for your input.

Output of broken code:
This the BEGINING of batch file 1.
This is a FUNCTION in batch file 1.
This the BEGINING of batch file 2.
This is a FUNCTION in batch file 2.

ERROR occured, do you want to CONTINUE? [Y/N] n

This is the END of batch file 2.
This is the MAIN of batch file 2.
Above should only be printed if you chose to continue.

This is the END of batch file 2.
This the END of batch file 1.
Press any key to continue . . .
Output with workaround applied:
This the BEGINING of batch file 1.
This is a FUNCTION in batch file 1.
This the BEGINING of batch file 2.
This is a FUNCTION in batch file 2.

ERROR occured, do you want to CONTINUE? [Y/N] n

This is the END of batch file 2.
This the END of batch file 1.
Press any key to continue . . .
Lab2-TestFile1.bat:

Code: Select all

@echo off
setlocal EnableDelayedExpansion
set extfile=Lab2-TestFile2.bat

REM ---- MAIN
echo This the BEGINING of batch file 1.
echo.
call :TestFunctionOne
echo.
call %extfile%
echo.
call :End

REM ---- FUNCTIONS
:TestFunctionOne
    echo This is a FUNCTION in batch file 1.
goto :EOF

:End
    echo This the END of batch file 1.
    pause
exit
Lab2-TestFile2.bat:

Code: Select all

@echo off
setlocal EnableDelayedExpansion

REM ---- MAIN
echo This the BEGINING of batch file 2.
echo.
call :TestFunctionTwo

REM ---- UGLY WORKAROUND, UNCOMMENT TO TAKE INTO USE
::if "%errorlevel%" NEQ "0" goto :EOF

echo.
echo This is the MAIN of batch file 2.
echo Above should only be printed if you chose to continue.
goto :End

REM ---- FUNCTIONS
:TestFunctionTwo
    echo This is a FUNCTION in batch file 2.
    call :GetUserInput
goto :EOF

:GetUserInput
    echo.
    set /p "choice=ERROR occured, do you want to CONTINUE? [Y/N] "
    if /i "%choice%" equ "Y" goto :EOF
    if /i "%choice%" equ "N" goto :End
    echo "%choice%" is not a valid choice, please try again^^!
goto :GetUserInput

REM ---- This funtion should ALWAYS return to batch file 1
:End
    echo.
    echo This is the END of batch file 2.
REM ---- GOES WITH THE UGLY WORKAROUND ABOVE, UNCOMMENT TO TAKE INTO USE
::exit /B 1
REM ---- Not always returning to batch file 1
goto :EOF
Last edited by Tanel (18 Jan 2011 16:23)

----------------------------

#2 18 Jan 2011 18:15
jeb


Hi Tan,

nice to meet you here.

I understand your problem as:
Exit a batch-file immediatly, even in nested functions.
I suppose the key could be the "start /b /wait cmd /c bat2.bat"

I tested it with these to batch files

bat1.bat

Code: Select all

@ECHO OFF
echo This is %~0
call :func
echo end of %~0
goto :eof

:func
echo func %~0 ... Now calling bat2
start /b /wait cmd /c bat2.bat
echo func %~0
goto :eof

bat2.bat

@ECHO OFF
echo     This is %~0
call :func
echo     end of %~0
goto :eof

:func
echo     func %~0 .... Now exit 
exit 
echo     func %~0
goto :eof
And it seems to work.

hope it helps
jeb

----------------------------

#3 19 Jan 2011 15:29
Tanel


Thank you jeb! That worked as a charm! Very much appreciated. :-)

All the best,
Tanel

----------------------------

#4 19 Jan 2011 17:59
avery_larry


How about simply:

exit /b

instead of exit?

----------------------------

#5 19 Jan 2011 18:26
jeb


It doesn't work, that's the cause.

In the most cases the behaviour of exit /b is good, but not here.
exit /b only exits the last "call/function"-level, but not the entire batch as Tanel wanted.

----------------------------

#6 19 Jan 2011 19:23
avery_larry


But exit /b does exactly as described -- oh just never mind. ;-)

----------------------------

#7 20 Jan 2011 11:29
Tanel


avery_larry, I tried "exit /b" before posting this thread and just as jeb pointed out, it does not work.

----------------------------

#8 21 Jan 2011 16:34
avery_larry


I wanted the same thing a long time ago. It forced me to understand how subroutines and call functions work, and then I was able to change my scripts to utilize exit /b and subsequent errorlevels.

I felt that using start "" cmd was a bit of a "sledgehammer" approach. Functional certainly, and I used it for a good while. But then I started using subroutines instead of separate batch files, I started using errorlevels, and I started using the && & and || functions to allow for a "chained" exit when appropriate.

But it's clearly in a category of personal preference. Each person will have a different idea of "elegant". So it's more of a philosophical discussion.

----------------------------

#9 24 Jan 2011 14:27
Tanel
avery_larry wrote:

I wanted the same thing a long time ago. It forced me to understand how subroutines and call functions work, and then I was able to change my scripts to utilize exit /b and subsequent errorlevels.

I felt that using start "" cmd was a bit of a "sledgehammer" approach. Functional certainly, and I used it for a good while. But then I started using subroutines instead of separate batch files, I started using errorlevels, and I started using the && & and || functions to allow for a "chained" exit when appropriate.
But it's clearly in a category of personal preference. Each person will have a different idea of "elegant". So it's more of a philosophical discussion.

Do I understand you correctly in that you'd rather use...

Code: Select all

call :TestFunctionTwo || goto :eof
...together with:

Code: Select all

exit /b 1
Yes, that would work and might even be the preferred way to do it when working with a single batch-file. However, I don't see it being more elegant when one _has to_ integrate multiple batch-files - as the case I above. My view is that in the case above "exit /b" would give the called batch file more jumps and make it more complex without giving anything back. I would love to hear about your views on this.

----------------------------

#10 24 Jan 2011 18:31
avery_larry


With multiple batch files, you can still do the same thing:

Code: Select all

call otherbat.cmd || goto :eof
And then otherbat.cmd can have a "normal" exit using goto :eof, or a "break out" exit with exit /b or a "kill everything error" exit with exit /b 1. You can even get fancy with different errorlevels.

Also, if you do any variable sharing, starting a new cmd will typically inherit the current variables, but any variable manipulation will be lost on the "exit" command. That's actually the primary factor on why I stopped using the "call cmd ..." and exit method. I needed to manipulate variables in the other batch files.

When I finally decided to figure out how to get rid of my "start cmd ..." coding, I ended up consolidating some of my script files -- using call labels for some. For other script files that would be used from multiple script files (where a call :label would have to be added to multiple scripts), I decided that I really wanted to have a more robust, generalized scripting concept. And that's the other big problem I have with the "start cmd ..." method is that you ALWAYS have to call that batch file using a "start cmd ..." command, otherwise the "exit" will kill everything instead of actually returning to the point where it was called. So the "generic" script concept would give you the concept of a normal exit (goto :eof) or an error exit (exit /b 1).

Of course, as I already said, that's all more about personal preferences. Many would say using common variables in script files is a very bad idea. Or having a called script file manipulate variables, yet that's precisely what I often do.

----------------------------

#11 24 Jan 2011 19:34
jeb


You could dynamically import/include your other batch files,
instead of calling them as external software, so you can use a "single" file solution again.

It's a little bit slower at startup, but in my opinion it's the best way for me.

----------------------------

#12 24 Jan 2011 20:06
Tanel
avery_larry wrote:

With multiple batch files, you can still do the same thing:

call otherbat.cmd || goto :eof

And then otherbat.cmd can have a "normal" exit using goto :eof, or a "break out" exit with exit /b or a "kill everything error" exit with exit /b 1. You can even get fancy with different errorlevels.
I do understand the general idea; I've been applying the same concept in .sh/.ksh/.csh/.bash-scripts for quite some time. However I cannot see it solving the issue at top and it seems jeb is of the same opinion. Could you be so kind and provide an example using the top code as a base?
Also, if you do any variable sharing, starting a new cmd will typically inherit the current variables, but any variable manipulation will be lost on the "exit" command. That's actually the primary factor on why I stopped using the "call cmd ..." and exit method. I needed to manipulate variables in the other batch files.
I can see the reasoning behind wanting to use variable sharing, that's more than a valid reason to use call instead of start. :-)
And that's the other big problem I have with the "start cmd ..." method is that you ALWAYS have to call that batch file using a "start cmd ..." command, otherwise the "exit" will kill everything instead of actually returning to the point where it was called. So the "generic" script concept would give you the concept of a normal exit (goto :eof) or an error exit (exit /b 1).
Yes, this is the problem I was having and the issues of using goto :eof is high-lighted in my first message. Unless you provide a code sample of how to solve the original problem (described at the very top) I unfortunately cannot come to any other conclusion than that you've misunderstood the issue.
Post Reply