100 Languages Speedrun: Episode 72: Windows Batch Files

Batch Files, with extension .bat, were DOS equivalent of shell scripts.

DOS version (still used by DOSBox) was extremely limited, and couldn't even add numbers. Starting with OS/2 and Windows NT, .bat files were extended with some very rudimentary scripting capabilities, so that's what we'll be doing today, but I'll limit my use of these extended features to just set /a for math.

You can run these examples on any Windows machine. Due to set /a they won't work on a DOSBox, or on a DOS machine if you somehow still have one.

Hello, World!

We can start by writing hello.bat:

echo Hello, World!

Unfortunately it does a very annoying thing, and echos every command before executing it. That's fine for debugging, but not really what we want most of the time:

C:\> hello

C:\> echo Hello, World!
Hello, World!

C:\>

We can modify this by putting @ before the command, or we can just disable command echo-ing completely with @echo off.

@echo off
echo Hello, World!
C:\> hello2
Hello, World!

C:\>

I'm still not sure where that extra newline is coming from, but let's not worry about it for now.

Variables

We can set variables with set name=value, and insert them into other commands with %name%.

@echo off
set name=Alice
echo Hello, %name%!
C:\> vars
Hello, Alice!

C:\>

This is about the point where we could still run those examples on DOSBox or a DOS machine. From this point on, it's all Windows extensions.

Math

DOS .bat files had no way to do math, but Windows added set /a for it:

@echo off
set /a a = 40
set /a b = 380
set /a c = %a% + %b%
echo %c%
C:\> math
420

C:\>

Loop

@echo off
set /a i = 1
:loop

echo %i%

set /a i = 1 + %i%

if %i% neq 11 goto :loop
C:\> loop
1
2
3
4
5
6
7
8
9
10

C:\>

What's going on here:

  • we set initial value of i to 1
  • we have a label :loop
  • every iteration we echo current value of i
  • then we increase i by 1
  • if i is not equal to 11, we jump to :loop

Fibonacci

Functions are hard, so let's do iterative solution. We just do a bunch of variable assignments and additions in a loop. We don't need any new features here:

@echo off
set /a i = 1
set /a a = 1
set /a b = 1
:loop

echo fib(%i%)=%a%

set /a i = 1 + %i%
set /a c = %a% + %b%
set /a a = %b%
set /a b = %c%

if %i% neq 21 goto :loop
C:\> fib
fib(1)=1
fib(2)=1
fib(3)=2
fib(4)=3
fib(5)=5
fib(6)=8
fib(7)=13
fib(8)=21
fib(9)=34
fib(10)=55
fib(11)=89
fib(12)=144
fib(13)=233
fib(14)=377
fib(15)=610
fib(16)=987
fib(17)=1597
fib(18)=2584
fib(19)=4181
fib(20)=6765

C:\>

FizzBuzz

@echo off
set /a i = 1

:loop

set /a fizzbuzz = %i% %% 15
set /a fizz = %i% %% 3
set /a buzz = %i% %% 5

set /a t = %i%
if %fizz% == 0 set t=Fizz
if %buzz% == 0 set t=Buzz
if %fizzbuzz% == 0 set t=FizzBuzz

echo %t%

set /a i = 1 + %i%
if %i% neq 101 goto :loop
C:\>fizzbuzz
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
...

We can do set conditionally just like we can do goto.

FizzBuzz, modern version

So far I used just DOS features and set /a. But Windows added a lot of features to .bat files - carefully gating them behind extra sigils and switches to maintain backwards compatibility.

If we wanted to use all the features, here's what FizzBuzz would be like:

@echo off
setlocal enableDelayedExpansion
for /l %%j in (1 1 100) do (
  set /a fizz = %%j %% 3
  set /a buzz = %%j %% 5
  set /a fizzbuzz = %%j %% 15
  if !fizzbuzz! == 0 (
    echo FizzBuzz
  ) else if !buzz! == 0 (
    echo Buzz
  ) else if !fizz! == 0 (
    echo Fizz
  ) else (
    echo %%j
  )
)

But at this point we might just as well use PowerShell.

Should you use Windows Batch Files?

No. If you need to script a Windows machine, PowerShell is already there for you. And if it's something more complex, you might just as well use a real programming language.

Code

All code examples for the series will be in this repository.

Code for the Windows Batch Files episode is available here.