Hach ja, Rekursion, das Lieblingstierchen jedes Programmierers. Sicher ist sowas auch in Batchdateien möglich (Ich versuche übrigens immer noch Turingvollständigkeit nachzuweisen :-)).
Der erste zaghafte Test wäre erstmal eine unendliche Rekursion:
Und siehe da, sie funktioniert:
Genau das, was wir haben wollten. Etwas sinnvolleres als einen Stacküberlauf hatten wir uns ohnehin nicht erhofft. Also offensichtlich kann cmd Rekursion.
Dann sollten wir das auch mal mit einem zumindest ansatzweise praxisbezogenem Problem testen: Fakultäten. Ungeachtet dessen, daß man die besser iterativ berechnet. Wir wollen aber nur sicherstellen, daß die Rekursion vernünftig funktioniert:
Wir brauchen hier leider eine temporäre Variable, da cmd keine Berechnungen
ohne SET /A erlaubt, aber ansonsten sieht es in
etwa so aus, wie es sollte. Der Rekursionsabbruch wurde am Anfang des
Unterprogramms durch ein IF
abgefangen, leider gibt es keine funktionalen Nettigkeiten wie verschiedene
Funktionsdefinitionen hier.
Und funktioniert das nun auch? Aber sicher:
Mein Taschenrechner sagt mir sogar, daß die Werte richtig sind. 12! ist leider die höchste Fakultät, die man damit berechnen kann, da wir auf 32-bittige vorzeichenbehaftete Ganzzahlen beschränkt sind. Ein kleiner Fehler ist noch vorhanden, wenn man negative Zahlen als Argument angibt (wieder eine unendliche Rekursion). Das ist allerdings in der angehängten Version behoben, ebenso bekommt man in selbiger eine hilfreiche Nachricht, wenn man die Batch ohne Parameter aufruft.
Und nur so nebenbei, eine nette Variante, Fakultäten zu berechnen, indem wir einfach den eingebauten „Taschenrechner“ von cmd benutzen:
Wir basteln uns hier einfach die komplette Berechnung in einer Zeile zusammen
und lassen die dann von SET
/A auswerten. Nichts großartig aufregendes aber wahrscheinlich
schneller als Rekursion.
Eine häufig gebrauchte Sache in einigen Algorithmen (besonders Sortieren) ist, zwei Werte miteinander zu vertauschen. Häufig sieht das dann in etwa so aus:
temp := a;
a := b;
b := temp;
In einigen Sprachen (wie beispielsweise Python oder Lua) kann man das wesentlich eleganter lösen, da es dort Tupel gibt:
a, b = b, a
Und tatsächlich ist eine ähnliche Methode auch in Batchdateien möglich.
Da Variablenersetzung in CMD in dem Moment geschieht, in dem eine Zeile gelesen wird (aus Kompatibilitätsgründen) außer, wenn man ! statt % verwendet (verzögerte Auswertung), kann man sich das zunutze machen, da dann in jedem Falle der Wert der Variablen eingesetzt wurde, bevor die Zeile ausgeführt wurde. Damit kann man nun auch Variablenwerte in einer Zeile tauschen, ohne eine temporäre Variable zu benötigen:
set A=%B%&set B=%A%
Man sollte vielleicht vor dem & kein Leerzeichen machen, wenn es darauf ankommt, daß die Werte tatsächlich nicht mit einem Leerzeichen enden (bei Zahlen, mit denen man rechnet, sollte dies aber egal sein).
Würde man hier ! statt % benutzen, würden beide Variablen auf den Inhalt von B gesetzt werden, da der alte Wert von A nicht mehr erhalten bleibt.
Man kann mit call andere
Batchdateien aufrufen und ihnen auch Parameter übergeben. Dieser Aufruf
funktioniert wie ein Unterprogramm. Gegeben nun den Fall, man hat einen Haufen
nützlicher Funktionen, möchte aber nicht immer eine eigene Datei für jede von
ihnen oder will nicht immer den Inhalt als Unterprogramm in andere Batchdateien
einfügen.
Der Code besteht hier im Wesentlichen aus ein wenig Initialisierung,
namentlich dem set target=%1 und shift, um das
Sprungziel zu erhalten und für folgenden Code alles normal aussehen zu lassen.
Das Unterprogramm get_param_list ist eigentlich nur dazu
da, daß man ein wenig komischen Code zum Anschauen hat sowie ein bißchen
Debug-Code fürs Testen. Der Rest sind nur Labels, die die Unterprogramme
markieren, die wir sammeln wollen, jedes wie üblich mit goto :EOF
abgeschlossen. Die Batch selbst ist also nichts weiter als eine Art switch-Anweisung, die die Funktion
auswählt, die man ihr mit dem ersten Parameter übergibt.
lib.cmd genannt):
Voilà, und schon haben wir eine Art Bibliotheken oder sogar Namespaces. Wenn man will, kann man das ganze immer noch schachteln, das lasse ich dann als Übung für den geneigten Leser.
Unterprogramme in Batchdateien sind relativ einfach zu bewerkstelligen.
Einfach ein Sprungmarke schreiben, ein goto :EOF und ein call :label. Nutzt das Unterprogramm jedoch ein setlocal-endlocal-Block, möchte man vielleicht
Variablenänderungen des Unterprogramms in den darüberliegenden
Gültigkeitsbereich weiterreichen, allerdings setlocal
beibehalten, falls man exzessiv lokale Variablen nutzt.
endlocal ausgeführt wird. Das set danach hingegen wird nach endlocal ausgeführt und beeinflußt demzufolge
schon wieder den äußeren Gültigkeitsbereich aber die Variable ist schon
ersetzt worden und folglich können wir hierüber den Gültigkeitsbereich
überschreiten.
Anmerkung: Ich habe diesen Trick bei Paul Sadowski schon gesehen und befand ihn des Merkens wert. Bislang habe ich es allerdings noch nicht allzu häufig benötigt. Meine geplante Bignum-Implementierung wird davon allerdings ausnehmend Gebrauch machen.
Wenn es etwas gibt, was die Windows-Kommandozeile (cmd.exe) ganz gut kann (außer das Starten anderer Programme), dann ist das Verarbeiten von Zeichenfolgen. Nicht gerade auf Perls Niveau, aber sicher schon angenehmer zu nutzen als die C-Standardbibliothek (Reguläre Ausdrücke lassen wir hier mal weg).
Ich spielte nur ein wenig herum und heraus kam folgendes:setlocal mit den üblichen Optionen (ich setze
das eigentlich schon aus Gewohnheit fast immer, egal ob ich es brauche oder
nicht). Alle Kommandozeilenargumente werden in einer Variablen gespeichert und
dann Zeichen für Zeichen auseinandergekommen. Sobald der Orignaltext leer
ist, können wir aufhören und das Resultat ausgeben.
Und es funktioniert sogar mit Unicode: