I’ve gotten some great feedback from the community on the last few posts. At some point, I’m considering integration of a comment engine like Disqus. While this content is static HTML, I could still embed such a solution in the site to facilitate comments. I’m trying to avoid the whole peanut gallery effect, which I don’t think adds anything, but I believe the benefit of allowing comments that add to to the conversation or allowing readers to easily ask questions would outweigh the possibility of misuse. This is something I will consider carefully over the next few weeks.
In Part III, we’re going to going to look more deeply at .bat
files with examples of mediocre ways of doing things and some suggestions for cleaning up a poorly written script and implementing it in a better way. We’ve already seen some of these techniques, but we’re going to start putting them all together in a more meaningful way. A lot of the little details you need to know to build solid .bat
files will provide context for more advanced scripts we’ll look at later. Also, despite their age, sometimes a quick and dirty .bat
file is just needed to get something done. There is no shame in this and being able to quickly whip up a good solution using one is a sign of a good Windows sysadmin, in my opinion.
Please examine our starting point, which is the .bat
file below:
cd c:\blarg\
blarg.exe
exit
Well, a couple of comments here— first, why are we using cd
to change directories? One common misconception with .bat
files is that we need to know the exact path of any executable we want to run. This is totally not true, as you can use %~dp0
as a batch parameter to refer to the directory containing the .bat
file that is executing. While cd
can be used to change working directly, it usually isn’t necessary since you can use a full or relative file path. Finally, putting an exit
at the end of the .bat
file is a bad practice since it can prematurely exit the shell that called the .bat
file, which may cause unintended consequences such as killing another .bat
file that called this one. Another example of where exit
maybe be used is when you want to return a numeric result to indicate the success or failure of the .bat
file as we did in Part I (exit %result%
for example). Otherwise, “ending” a .bat
file in this manner is unnecessary. A cleaner implementation might be as follows:
%~dp0blarg.exe
Keep in mind, our directory structure should be as follows:
C:.
└───blarg
blarg.bat
blarg.exe
Now, there’s a few more problems that can crop up. If we were to, for example, move the blarg
folder into c:\program files\
we’d still have a problem:
C:\Program Files\blarg>blarg.bat
C:\Program Files\blarg>C:\Program Files\blarg\blarg.exe
'C:\Program' is not recognized as an internal or external command,
operable program or batch file.
C:\Program Files\blarg>
As you may have guessed, the space in c:\program files\blarg
is causing us some heartache. In order to account for this, we need to update blarg.bat
as follows:
"%~dp0blarg.exe"
Next, you are probably wondering what happens if blarg.exe
fails for some reason. Does it generate some output that is important that is sent to STDOUT
or STDERR
? The next obvious thing to do is, as we mentioned in Part I of this series, might be to redirect all of this output to a log file:
"%~dp0blarg.exe" >> "%~dp0log.txt" 2>&1
Now log.txt
should begin capturing output on the first and subsequent runs in the blarg
folder. Additionally, future runs will always append to the existing log file, which may or may not be desirable. One can always use overwrite (>
) instead of append (>>
), if needed.
At this point, we’re really starting to get somewhere. But maybe we’re interested doing something based on what the return code is from blarg.exe
as we did in Part I. Perhaps the scenario is that we need to conditionally run blarg2.exe
if blarg.exe
fails:
"%~dp0blarg.exe" >> "%~dp0log.txt" 2>&1
if not "%exitresult%"=="0" goto fail
goto :eof
:fail
"%~dp0blarg2.exe" >> "%~dp0log.txt" 2>&1
One thing to note here is goto
is not like calling a function. We simply skip to the specified label (:fail
) and continue execution. Or in the case of :eof
, this label is a special built-in label that ends the .bat
file.
Finally, we might have a need to run blarg2.exe
multiple times. Perhaps we need to run it over and over with a different set of parameters each time. What we could do is store some values for one of those parameters in a text file. Using the for
command, we can do just that:
set %hostsFile%="%~dp0hosts.txt"
set %secondParameter="blargityBlarg"
"%~dp0blarg.exe" >> "%~dp0log.txt" 2>&1
if not "%exitresult%"=="0" goto fail
goto :eof
:fail
for /f %%a in (%hostsFile%) do "%~dp0blarg2.exe" --firstParameter=%%a --secondParameter=%secondParameter% >> "%~dp0log.txt" 2>&1
Our hosts.txt
, by the way, should look like this:
someHost
someOtherHost
thisIsAnotherHost
yayOneMoreHost
With these simple .bat
concepts and the understanding that most of these concepts transfer to other scripting languages (even on other platforms), I hope I have provided something that may be of use to you in the future. While .bat
files have served for decades in Windows and MS-DOS as quick and dirty solutions for getting things done, they aren’t always the most elegant or maintainable solution to every problem. This statement is a prelude to Part IV, where we tackle much more complicated scripts and automation.
Comments !