Aah, recursion, the favorite pet of every programmer. Surely it must be possible to recurse even in Windows batch files (I'm still trying to prove Turing-completeness, by the way :-)).
The first tentative test would be an infinite recursion:
And know what? It works. Well, kinda:
But that's ok, we didn't expect this to do anything useful except of causing a stack overflow. But as we can see, cmd has a stack of some sort and seemingly manages it well enough to allow for recursion.
Time for another test, this time something remotely practical: Factorials. Never mind that those are more easily done with iteration, we want to make sure that recursion works properly:
We need a temporary variable at the end, unfortunately, since cmd does not allow computations inline. But aside from that it looks pretty much how it should. The case for breaking the recursion is also provided in the form of an IF block (sorry, no functional programming niceties, like different function definitions).
But does it work? Oh, sure it does:
And my calculator tells me that those are actually correct. 12! is unfortunately the highest factorial we can compute with it, since we are limited to 32-bit signed integers. A minor bug is still present when using negative numbers, though (infinite recursion, again). This is corrected in the attached version (as well as giving a helpful hint when running the batch without arguments).
Just as a side note, a fun way to implement factorial calculation by leveraging cmd's own “calculator”:
We simply construct the complete term and evaluate that by using SET /A. Nothing fancy, but probably faster than the recursion.
You can call other batch files via the call command and pass parameters as well. Well, suppose you have a plethora of useful functions but you don't want a batch file for each of them or copy them into all batches that use them.
With a simple pattern you can concentrate them into one batch file. I've prepared this here, along with a bit of stuff you might need to do to ensure it's working:
The code above consists of a bit initialization code, namely the set target=%1 and shift to get the label we want to jump to and return everything to normal for the following code. The get_param_list is mainly there so we have some weird code to look at and have a bit of debugging stuff in place for testing. The rest are labels that mark the subroutines, ending with a goto :EOF each. The batch itself is nothing more than some kind of switch statement, selecting the function to execute with the first parameter.
lib.cmd):
Voilà, we got some kind of libraries or namespaces. If you want, you can nest them, I leave that as an exercise for the reader.
Creating subroutines in batch files is easy. Just create a label, a goto :EOF and call :label. However, if the subroutine uses a setlocal-endlocal block you might want to be able to pass variable changes out of that block and still retain the local scope to avoid pollution of the upper scope.
You can exploit the fact that cmd expands variables on reading a line before actually executing it:
The trick here is that variable expansion takes place before endlocal (or the set following it) is executed and set is executed after endlocal and thus affects the outer scope, but is able to pass a value of the inner scope along.
Note: I've seen this trick before at Paul Sadowski's site and found it worth remembering, although I didn't use it that much so far (my Bignum implementation will, however make extensive use if it).
Arrays are frequently used in programming and nearly all programming languages have something that can be used as such, nevermind what it's called. Arrays, tables, lists, etc. are all just a means of organizing large amounts of similar data.
In batch files we have to do a little trickery to achieve something one might call arrays. There are at least two possibilities that spring to my mind instantly:
The easiest and most flexible solution I've come up with so far is simply to create a variable for each entry, all of them share a common prefix (which may be empty). So, to create an array with, say, 100 members, this can be done quickly with
for /l %%i in (1, 1, 100) do set ARRAY%i=0
That way we have an array, called ARRAY (the prefix, I usually use it as a name) with 100 variables in it, each initialized to 0.
Array access is simple in just accessing the appropriate variable, it has, however, a few issues:
echo %ARRAY2%
echo !ARRAY%NUM%!
for /l %%i in (3, 1, 5) do echo !ARRAY%%i!%ARRAY%NUM%% won't work since cmd's parser gets confused with the %. And % variables will be expanded when the line is read, not when it's executed.
set /a SUM=ARRAY%X%+ARRAY%Y%If you insist, you can also use a notation familiar with other programming languages, [ and ] are perfectly legal as characters in environment variables (if I remember correctly the only illegal characters are = and the null byte).
If you know which characters your array variables will contain you can also use a long string with a separator character:
set ARRAY=1,2,3,4,5,6,7,8,9,10
This requires you to use for /f whenever you need to access a value within the array, write-access to values is very inconvenient (essentially you have to either search for the right indices within the string and do substring magic or you rebuild the entire array each time you change or delete a value.
To sum it up: I always used the first variant, since it's pretty easy and depending on your application deleting values (and tedious copying) might not be necessary or even wanted (my Sieve of Eratosthenes simply kept only the values it was interested in and used if defined to check for them.
A common task in many algorithms is swapping two values. Usually implentations take the form
temp := a;
a := b;
b := temp;
In some languages (such as Python or Lua) you may also write the following:
a, b = b, a
And a method similar to this one is even possible in batch files.
Since variable substitution in CMD is done while the line is parsed (for backwards compatibility) except when using ! instead of % we can use this to our advantage and swap to values in one line without resorting to a temporary variable:
set A=%B%&set B=%A%
Be careful not to use a space before the & to avoid having that space character in your value (if this is critical).
And the use of % here is also important. Since delayed expansion (done with ! expands when the value is used the code set A=!B!&set B=!A! will set both A and B to the value of B.
If there is one thing the Windows Command Processor (cmd.exe) can do (except starting other programs) it's string processing. Not at Perl's level but certainly more sohpisticated than the C standard library (not counting regex here).
I was just playing around a bit and came up with this:
setlocal with the usual options (I almost always set them, regardless whether I need them or not). Saving all command line arguments into a string and then dissecting it character by character. As soon as the original string is eaten up we can quit and output the result.
And it works in Unicode, too:
When writing a loop sometimes it can become necessary to break out of the loop before it is finished. Batch files allow loops, but do they allow some kind of break statement?
Actually, yes:
if statement checks for the loop variable being greater than 10 and based on that will either break or print the number. And as you can see, if we would let the loop finish we wouldn't see any message since we quit immediately after the loop.
Does this work with subroutines as well? Sure:
goto :EOF), so when branching out of the subroutine we would print "successfully broken the loop" but return to looping directly thereafter.