Windows Batchfile Programming Notes - Part 1

I’m converting a shell script to a Windows batchfile and had to search up a few things to fill in the gaps. These are notes for me to be able to find these details again.

Comments

This is simple.

  • Official way to have comments: REM
  • Also, supported way: ::

Echo a Blank Line

You use ECHO some text to print a line on the console. I like to have a few blank lines between paragaphs in a wall of text. Doing just echo does nothing useful in this case since it returns the status of whether echo-ing is turned on or off (e.g., ECHO is on or off).

The way to handle this is echo. this gives you a blank line.

If this is your script blank.cmd:

@echo off
echo Line before blank
echo.
echo Line after blank

You get this output:

$ blank.cmd
Line before blank

Line after blank

Check if an environment variable is defined

In a shell script, you’re likely to see:

# Make sure SPECIAL_ENV_VAR is set
if [ -z "$SPECIAL_ENV_VAR" ]; then
    echo "Error: 'SPECIAL_ENV_VAR' not set."
    exit 1
fi

You can do this instead:

:: Make sure SPECIAL_ENV_VAR is set
if not defined SPECIAL_ENV_VAR (
  echo SPECIAL_ENV_VAR not set. Cannot proceed.
)else (
    echo SPECIAL_ENV_VAR set. Proceeding.
)

Be very careful about 2 things:

  • If a ) appears at the end of a line, it may be seen as the end of the if segment. So, avoid statements like echo (see readme.md)
  • The ) that closes the if should always come after a line/ command that is not commented out

Echo multiple lines to a file

Sometimes, you want to able to output multiple lines to a file. The way to do this is to put the multiple echo statements between parentheses as below.

@echo off
echo The rest of the lines below will be written to file.txt
(
echo The path currently is:
echo SYSTEMROOT=%SystemRoot%
echo.
echo That looks good.
) > file.txt

This is what file.txt looks like when you run it:

$ type file.txt
The path currently is:
SYSTEMROOT=C:\WINDOWS

That looks good.

Of course, you can append into the file (instead of replacing its contents) by doing >> file.txt instead of > file.txt

Exit the script, not CMD.exe

It’s common to have a exit in the scripts. In Windows command files, this will exit the command shell that you’re running from. You can use exit /B <return-code> instead to just exit the script not the shell.

exit /B 1

Standard Output and Standard Error Streams

You’ll often come across code that looks like 2>&1 which works the same in Windows. It will send the stderr (2) to the same place as stdout (1). Strictly speaking, it will send the data to a copy of the handle used by stdout.

Some common usage is as below:

  • This will send the output to file.txt and will also redirect all errors (2) to a copy of stream 1, i.e., to file.txt.
$ my_command.exe > file.txt 2>&1
  • You can explicitly set 1 and 2 in the same way:
$ my_command.exe 1> file.txt 2>&1
  • You can send both to NUL (same as /dev/null) by doing:
$ my_command.exe > NUL 2>&1

Note that since 2>&1 asks to send it to a copy of the stream for 1, the position matters. If you do 2>&1 1>NUL, it means that 2 will go the console and 1 will thereafter go to NUL. If you do 1>NUL 2>&1 it means that 1 will go to NUL and 2 will thereafter also go to 1, i.e., both go to NUL. There is more on this on stackoverflow

Find which executable(s) are on path

Linux/ Unix users will often tell you do which to find the executable on the path. Windows uses where instead and was the cause of my first patch to Rails. Here is the simple use of it.

$ where notepad
C:\Windows\System32\notepad.exe
C:\Windows\notepad.exe

$ where ruby.exe
D:\Ruby33-x64\bin\ruby.exe
D:\Ruby32-x64\bin\ruby.exe

$ where where.exe
c:\Windows\System32\where.exe

< -- Let's nuke the path so that ruby is no longer on the path -- >

$ path=c:\windows\system32
$ where ruby.exe
INFO: Could not find files for the given pattern(s).

Couple of other helpful things

There are a couple of other things that I had previously documented:

Hope this helps. I expect that there will be a second part at some point if I need to do this more although I often just end up with a simple Ruby script instead of jumping through bash/ shell/ command scripts.

comments powered by Disqus