Batch command exit - batch-file

I have to rewrite an old Batch file into Powershell script.
one line is
directory1\nunit-console.exe file1.dll /xml:results.xml
exit %%ERRORLEVEL%%
I am a bit confused about the "exit %%ERRORLEVEL%%" bit
Normally, just type in command line >>exit and enter. It will shut the screen.
What does %%ERRORLEVEL%% bit mean?
thanks

In "DOS" shells (i.e. COMMAND.COM, CMD.EXE), variables are defined as %VAR%. %ERRORLEVEL% represents the return value of the last-executed command; it is like $? in Bash or Perl.
In this case, exit %%ERRORLEVEL%% appears to be an attempt to exit the batch script with the return value from the call to nunit-console.exe. (This script looks quite old, as both exit and the need for double-percent symbols has changed slightly between MS-DOS and the later shells in Windows 2000 and later.)
See this page for a discussion of how exit is used in the various shell versions.
This MS KB article talks about using the % character in batch files. It was written for MS-DOS, but most of the same principles apply to later shells.

Related

How do I handle CMake.exe errors

I have a batch file that starts CMake.exe with some arguments and I need to break the execution if there were any errors in CMake (that's a part of product build process). The problem is that %ERRORLEVEL% from CMake.exe process seems to be always is 0 whereas stadard error output might contain errors such as
...
-- Configuring done
CMake Error at CMakeConfigs/My.cmake:77 (add_library):
Cannot find source file:
Resources/ActionIcons/ActionIcon_ABC.png
Tried extensions .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp
.hxx .in .txx
Call Stack (most recent call first):
My/Path/CMakeLists.txt:149 (my_add_target)
-- Generating done
...
I plan to dump standard error output to a file and grep/findstr error, but can error handling be done more elegantly?
Thanks.
The most often mistake is using %ERRORLEVEL% in a command block starting with ( and ending with matching ).
The entire command block is preprocessed by Windows command interpreter before running any command in the command block or the IF or FOR command usually used on line with opening (. During this preprocessing all %variable% references are replaced by current values of the referenced environment variables. Then the command block is executed with the preprocessed code.
This behavior can be seen by removing #echo off at top of the batch file, or modify it to #echo on or rem #echo off and run the batch file from within a command prompt window. Now Windows command interpreter outputs to console window every command line and command block after preprocessing as really executed next.
The help of command SET output on running set /? in a command prompt window explains on an IF and a FOR example how to reference an environment variable within a command line or command block modified in same command line or command block on execution using delayed environment variable expansion.
But for this case there is no need for %ERRORLEVEL% expanded during preprocessing phase or delayed expanded using !ERRORLEVEL! at all.
There is the good old IF ERRORLEVEL X ... syntax working since MS-DOS up to currently latest Windows 10.
CMake exits with exit code 0 on success and a greater number on error as nearly all console applications.
IF ERRORLEVEL X ... means IF exit code (return value of function main) assigned to environment variable ERRORLEVEL is greater or equal X THEN ...
Then I suggest to look on:
What are the ERRORLEVEL values set by internal cmd.exe commands?
Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?
A successful IF does not modify value of ERRORLEVEL. And EXIT /B without a number also does not modify ERRORLEVEL.
So on next line after the command line running CMake there is only this command line needed to exit the processing of current batch file and return to calling batch file or process on any error encountered by CMake:
if errorlevel 1 exit /B
The calling batch file or process gets the exit code of CMake as a result of the batch file execution because IF and EXIT don't modify ERRORLEVEL in this case. It's really so easy!

Batch file: pass a block of commands instead of a script.tcl

I run a .tcl script using a .bat file as per the simplified example below:
script.tcl
set a "this is the script"
puts $a
script.bat
echo off
set tclpath="C:\tclsh.exe"
set filepath="C:\script.tcl"
%tclpath% %filepath%
I wonder whether I can include in the .bat file the commands of the .tcl script, so instead of having two files I just have one .bat file that runs tclsh.exe and passes the commands to the tcl shell.
Is this possible? and how can I do it?
For many things, there are pages on the Tcler's Wiki that can be looked to for interesting things. In particular, this old page has some really useful techniques. As you read through, you'll see a history of techniques tried. They depend on the fact that Tcl commands can be prefixed with :: usually (marking them as a weird label in the batch file language) and you can comment out blocks of code in Tcl with if 0 (with Tcl not parsing the contents, beyond ensuring that it is brace-balanced, which code usually is).
The best technique is one that doesn't just make the code multilingual, but also makes it easily readable. Preserving readability is the key to not going crazy.
::if 0 {
#rem This code in here is pure batch script
echo off
set tclpath="C:\tclsh.exe"
%tclpath% "%~f0" %*
#rem Put this at the end; it means terminate since the “eof” label is end-of-file
#goto eof
}
# This code is the pure Tcl code
set a "this is the script"
puts $a
The other bits to be aware of:
"%~f0" — This gets the full path to the “zero-th argument”, which is the name of the script you're running.
%* — This is all the remaining arguments. It's a good idea to pass them on, and you can access them from Tcl using the list in the global argv variable.
I wonder whether I can include in the .bat file the commands of the .tcl script, so instead of having two files I just have one .bat file
that runs tclsh.exe and passes the commands to the tcl shell.
Easy peasy. . .
You can use a CALL to a subroutine in a batch script that will append the commands to the dynamically created script file which you specify with the set filepath variable.
This way you have everything in the batch script and you do not need to worry about the tcl script file other than ensuring the :tclshScript routine that creates it has the correct syntax, etc.
You essentially build the tcl script logic with batch ECHO commands and it'll create it per run.
Use caution with special characters though as the carat ^ symbol may be needed to escape certain character to the tcl script if batch interprets those otherwise or you notice an issue.
echo off
set tclpath="C:\tclsh.exe"
set filepath="C:\script.tcl"
IF EXIST "%filepath%" DEL /Q /F "%filepath%"
CALL :tclshScript
%tclpath% %filepath%
EXIT
:tclshScript
ECHO set a "this is the script">>%filepath%
ECHO puts $a>>%filepath%
GOTO EOF
Further Resources
CALL
ECHO
Escape

Catching return value from Tcl in batch

This is my Tcl script
return 2
It is doing nothing, but just returning value 2. Now my batch script call this tcl file called foo.
tclsh foo.tcl
The return value can change from time to time and I need to get this value in the batch file. Is thr a way to do that. I am new to batch commands, so I dont have much idea about this. I tried if-else loop, but it gives syntax error. Any idea would be appreciated.
To get an external caller of Tcl to see a result code, you need exit and not return:
# In Tcl
exit 2
Then your caller can use the exit code handling built into it to detect. For example with bash (and most other Unix shells):
# Not Tcl, but rather bash
tclsh foo.tcl
echo "exit code was $?"
On Windows, I think it's something to do with ERRORLEVEL but it's a long time since I used that platform for that sort of thing. I just remember it being annoying…
It appears %ERRORLEVEL% is what you want:
C:\Users\glennj>tclsh
% exit 3
C:\Users\glennj>echo %ERRORLEVEL%
3

To "Call" or "Not to Call" a batch file?

If from inside a bat file you called another batch file but still had a few remaining operations to complete, how can you make sure that the call to first bat file will after completion or error, will return to the file that called it in the first instance?
Example:
CD:\MyFolder\MyFiles
Mybatfile.bat
Copy afile toHere
or
CD:\MyFolder\MyFiles
CALL Mybatfile.bat
COPY afile toHere
What is the difference between using CALL or START or none of them at all? Would this have any impact on whether it would return for the results of the copy command or not?
As others have said, CALL is the normal way to call another bat file within a .bat and return to the caller.
However, all batch file processing will cease (control will not return to the caller) if the CALLed batch file has a fatal syntax error, or if the CALLed script terminates with EXIT without the /B option.
You can guarantee control will return to the caller (as long as the console window remains open of course) if you execute the 2nd script via the CMD command.
cmd /c "calledFile.bat"
But this has a limitation that the environment variables set by the called batch will not be preserved upon return.
I'm not aware of a good solution to guarantee return in all cases and preserve environment changes.
If you really need to preserve variables while using CMD, then you can have the "called" script write the variable changes to a temp file, and then have the caller read the temp file and re-establish the variables.
call is necessary for .bat or .cmd files, else the control will not return to the caller.
For exe files it isn't required.
Start isn't the same as call, it creates a new cmd.exe instance, so it can run a called batch file asynchronosly
The `CALL' statement was introduced in MS-DOS 3.3
It is used to call other batch files within a batch file, without aborting the execution of the calling batch file, and using the same environment for both batch files.
So in your case the solution is to use CALL
Okay, I actually didn't even really think about the fact that if you call a batch (regardless of the 'type', i.e. '.bat', or '.cmd') that it won't return if you don't use call.
I've been using call myself though for a different reason that I am actually pretty surprised that no one else has brought up. Maybe I missed it. MAYBE I'M THE ONLY ONE IN THE WORLD WHO KNOWS!! :O
Probably not, but I'm going to drop this knowledge off here because it's super useful.
If you use call you can use binary logic operators to decide how to proceed based on the ERRORLEVEL result. In fact, I always was flabbergasted on how && and || existed in DOS and COULDN'T be used this way. Well, that's why.
The easiest way to test this is to create a return.cmd with notepad, or from the command prompt like so:
c:\> type con >return.cmd
You will now notice the cursor goes down to the next line and hangs. Enter:
#exit /B %1
And then hit ENTER, and then CTRL-Z and that file will be created. Good! You may now feel free to try the following two examples:
call return.cmd 0 && echo Huzzah! A Complete Success! (Or cover up...)
call return.cmd 123 || echo Oops! Something happened. You can check ERRORLEVEL if you want the tinest amount of additional information possible.
So what? Well, run them again with the 0 and the 123 swapped and you should see that the messages DON'T print.
Maybe this multi-line example will make more sense. I use this all the time:
call return.cmd 0 && #(
echo Batch says it completed successfully^^!
) || #(
echo Batch completed, but returned a 'falsey' value of sort.
call echo The specific value returned was: %ERRORLEVEL%
)
(Note the 'call' in the || section before the second 'echo'. I believe this is how people got around not having delayed expansion back in the day. If you DO have delayed expansion enabled (via. setlocal EnableDelayedExpansion inside a batch OR launch a command prompt with cmd /v:on then you can just do !ERRORLEVEL!.)
... This is where I have to apologize and say if you have if ERRORLEVEL trauma in your past you should stop reading. I get it. Trust me. I thought about paying someone on fiverr to remotely type this for me, but for completeness sake I'm just going to take one for the team and mention that you can also do the following to check errorlevel:
if ERRORLEVEL 123 #echo QUICK! MOTHERS, COVER YOUR CHILDREN'S EYES! FINGERS ARE BEING UNDONE! :'(
If you've never typed that before then GOOD! You will live longer without having to read up why exactly you aren't getting the results you expect. Cruel is the word you're looking for, not 'quirky'.
The important part that I really want to get across however is that if you try this and DON'T use 'call' it will ALWAYS execute the 'true' branch. Try it for yourself!
If I'm missing something, or you know a better way to do this, please let me know. I love learning stuff like this!
Additional information I mentioned:
I have known for quite some time that you can put redirects BEFORE commands like so:
>nul echo. This won't be displayed!
But I accidentally discovered the other day by being a dumdum that you can apparently also do:
echo A B>file.txt C
And was REALLY surprised to find a file.txt which consisted of "A B C". It appears yo can place them ANYWHERE, even inside the command. I've never seen anyone do this, nor mention it, but I HAVE seen people mention that you can prefix a line with them.
Maybe it's a bug exclusive to Windows 10 or something. If you have another version and wanna try it out and let me know I'd be interested in what you find out.
Stay nerdy!

Why does calling a nested batch file without prepending "call" to the line exit the parent batch file?

I understand how to call nested batch files from within a parent file using the call command, as there are plenty of resources on that:
CALL
CALL (SS64)
Bat file termination
However, I don't understand why calling another batch file from another terminates the parent.
For a less abstract example, suppose I have a batch file that "links" together separate batch files, and I erroneously didn't prepend call to each line:
foo.bat
bar.bat
This would only execute foo.bat and then exit. To correctly execute both commands, I would have to prepend call before each statement:
call foo.bat
call bar.bat
Why does the first functionality still exist? Why hasn't it been changed? I noticed that call was introduced in MS-DOS 3.3, which was released in the late 1980s, so is this functionality still here for reverse compatibility?
I can't think of any (practical) usages of it, but perhaps I'm too used to "new" programming techniques.
DOS used simple text processing (back when you had things like FILES=20 in config.sys to allow 20 file handles), so opened the file, read the next line, closed the file, then executed the line just read. If the file called another, then the processing continued with that file, so only 1 file handle would be required for a batch file.
Until Microsoft put in the call command, there was no way to get back to the original file (without using tricks like giving the name of the previous file as a parameter, and using temporary files to let the original batch file know it had dome some processing, and could then GOTO the next part of the file).
As Sean Cheshire wrote, it's necessary for backward compatibility.
But starting a batch file from a batch file without using CALL does not terminate the parent!
It looks that way, as the parent normally will not executed further after the second batch exits.
But using a call before starting the second.bat, will show that the first batch isn't terminated.
parent.bat
echo parent.bat
call :myLabel
echo back in parent.bat main
exit /b
:myLabel
second.bat & echo back in parent.bat
exit /b
second.bat
echo second.bat
exit /b
I use here the the secpond.bat & echo back ... to avoid another bug/feature of cmd.exe.
If you use second.bat without any extras it will start second.bat AND jump to the label :myLabel in second.bat!
Call is basically saying "go execute this other batch file, and then come back here and continue". It has been there since DOS 3.3 or so, and if it were removed now would break all backward-compatibility (which is why people are still using batch scripts). It can also be used to branch to :link locations.
For info on the use and syntax (for future reference for others), you can see this MS TechNet link
If you need new functionality, use CMD scripts or PowerShell scripts instead.

Resources