This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Netzwerke, Betriebssysteme, Sicherheit ... hierzu bietet Ihnen die Reihe net.com umfassende, praxisnahe Information. Neben Fragen der Systemverwaltung greift sie auch Themen wie Protokolle, Technologien und Tools auf. Profitieren Sie bei Ihrer täglichen Arbeit vom Praxiswissen unserer erfahrenen Autoren.
Windows Server 2003 Eric Tierling 1312 Seiten, € 59,95 [D] ISBN 3-8273-2076-3 Ausführlich widmet sich dieses Buch allen wesentlichen Aspekten von Windows Server 2003 und all seiner Editionen. Active Directory, Gruppenrichtlinien, Windows NTDomänenupgrade, TCP/IP-Dienste und Sicherheitsmerkmale sind detailliert beschrieben, um Unternehmen einen optimalen Einsatz zu ermöglichen. Clustering mit Netzwerklastenausgleich und Clusterdienst, E-Mail-Server, Group Policy Management Console, Terminaldienste, Remotedesktop und Webverwaltung, Volumen-Schattenkopie sowie die Smartcard-Integration und sichere Wireless-LAN-Unterstützung stellen weitere Highlights dieses Buches dar.
Firewalls illustriert Jörg Fritsch, Steffen Gundel 320 Seiten, € 44,95 [D] ISBN 3-8273-2043-7 Der Titel ist Programm – mithilfe von weit mehr als 100 Abbildungen erläutern die Autoren den Aufbau einer Firewall. Sie fangen mit einfachen Access-Listen an, und erweitern den Aufbau der Firewall sukzessive, damit Sie genau nachvollziehen können, wie eine Firewall Ihr Netzwerk schützt und welches dafür die optimale Größe ist.
Holger Schwichtenberg
Windows 2003 Shell Scripting
eBook Die nicht autorisierte Weitergabe dieses eBooks ist eine Verletzung des Urheberrechts!
An imprint of Pearson Education München • Boston • San Francisco • Harlow, England Don Mills, Ontario • Sydney • Mexico City Madrid • Amsterdam
Bibliografische Information Der Deutschen Bibliothek Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über abrufbar.
Die Informationen in diesem Produkt werden ohne Rücksicht auf einen eventuellen Patentschutz veröffentlicht. Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutzt. Bei der Zusammenstellung von Texten und Abbildungen wurde mit größter Sorgfalt vorgegangen. Trotzdem können Fehler nicht vollständig ausgeschlossen werden. Verlag, Herausgeber und Autoren können für fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung übernehmen. Für Verbesserungsvorschläge und Hinweise auf Fehler sind Verlag und Herausgeber dankbar. Alle Rechte vorbehalten, auch die der fotomechanischen Wiedergabe und der Speicherung in elektronischen Medien. Die gewerbliche Nutzung der in diesem Produkt gezeigten Modelle und Arbeiten ist nicht zulässig. Fast alle Hardware- und Softwarebezeichnungen, die in diesem Buch erwähnt werden, sind gleichzeitig auch eingetragene Warenzeichen oder sollten als solche betrachtet werden. Umwelthinweis: Dieses Buch wurde auf chlorfrei gebleichtem Papier gedruckt.
Warum dieses Buch? Wer sollte dieses Buch lesen? Buchgliederung Hinweise zum Buch 1.4.1 Die Windows-Versionen 1.4.2 Schriftarten und Layout 1.4.3 Syntaxkonventionen 1.4.4 Stil und Anrede 1.4.5 Weblinks, Warenzeichen Feedback, Download der Beispiele Danksagungen
Was bedeutet Shell? Grundlagen Shell Scripts oder Batch Files? Shell starten 2.4.1 Die Befehlserweiterungen Arbeiten mit der Kommandozeile 2.5.1 Befehle eingeben 2.5.2 Kommandotypen 2.5.3 Behandlung von Sonderzeichen Hilfe erhalten 2.6.1 Hilfe zu einzelnen Befehlen 2.6.2 Das Kommando help 2.6.3 Die Online-Hilfe Verzeichniswechseln 2.7.1 cd (chdir) 2.7.2 pushd/popd 2.7.3 Aktuelles Verzeichnis feststellen Wo ist command.com?
Shell-Konfiguration 3.0.1 3.0.2
Tastenkombinationen Startparameter
31 31 32
5
Inhaltsverzeichnis
3.1
3.2
3.3
4
43
4.1 4.2
43 43 43 45 46 46 46 47 48 49 49
Dateikanäle Dateiumleitung 4.2.1 Ausgabeumleitung 4.2.2 Eingabeumleitung 4.2.3 Umleitung und der Windows Script Host Datenübergabe und Filter 4.3.1 Das Prinzip 4.3.2 find 4.3.3 sort 4.3.4 more 4.3.5 clip
Das Shell-Fenster einrichten 3.1.1 Optionen 3.1.2 Schriftart 3.1.3 Layout 3.1.4 Farben 3.1.5 Startverzeichnis 3.1.6 Integration in den Explorer Registry Keys 3.2.1 HKEY_LOCAL_MACHINE-Einträge 3.2.2 AutoRun 3.2.3 EnableExtensions 3.2.4 DelayedExpansion 3.2.5 CompletionChar 3.2.6 PathCompletionChar 3.2.7 DefaultColor 3.2.8 HKCU\Console Settings Laufzeitkonfiguration 3.3.1 color 3.3.2 Prompt 3.3.3 Title
Warum ist type unbrauchbar? NTFS Alternate Data Streams 5.2.1 Einführung 5.2.2 NTFS-Dateien 5.2.3 Erzeugen und Abfragen von ADS 5.2.4 Auflisten und Löschen von ADS 5.2.5 Verknüpfungen und ADS 5.2.6 Problemfeld Makro-Viren 5.2.7 Weitere Informationen Echte Links
Inhaltsverzeichnis
6
Umgebungsvariablen
63
6.1
63 63 63 64 65 65 66 68 69 70 71 75 75
6.2
7
Kommandoausführung
77
7.1
77 77 78 79 81 82 82 82 82 85 88
7.2 7.3 7.4
8
9
Grundlagen 6.1.1 Umgebungstypen 6.1.2 Auflisten von Variablen 6.1.3 Vordefinierte Variablen Arbeiten mit Variablen 6.2.1 Umgebungsvariablen mit set definieren 6.2.2 Arbeiten mit setx 6.2.3 Löschen von Variablen 6.2.4 Rechnen mit set (Option /a) 6.2.5 Eingaben mit set (Option /p) 6.2.6 Ausgabe von Variableninhalten 6.2.7 Die Variable errorlevel 6.2.8 setlocal & endlocal
Ausführbarkeit von Dateien 7.1.1 Ausführbare Dateien mit pathext definieren 7.1.2 assoc & ftype Das Kommando start Das Kommando runas Prozessverwaltung mit der Shell 7.4.1 tlist oder tasklist? 7.4.2 Prozessinformationen 7.4.3 tlist 7.4.4 tasklist 7.4.5 kill und taskkill
Befehlsverknüpfung
89
8.1 8.2 8.3 8.4
89 89 90 90
Mehrfachkommandos Der Operator && Der Operator || Befehlsgruppierung
Ablaufsteuerung 9.1
9.2
Bedingte Befehlsauführung 9.1.1 Grundlagen von if 9.1.2 Formen von if Das mächtige for 9.2.1 Grundlagen 9.2.2 Einfache Schleifen mit Werte- und Dateilisten 9.2.3 Variablenersetzung bei Schleifen 9.2.4 Schleifen für Verzeichnisse und Unterverzeichnisse 9.2.5 Zählschleifen 9.2.6 Lesen aus Dateien
93 93 93 94 96 97 97 99 102 103 103
7
Inhaltsverzeichnis
9.2.7 9.2.8 9.2.9
Parsing von Zeilen Backquoting Tipps & Tricks
10 Makros 10.1 Grundlagen 10.2 Arbeiten mit Makros 10.2.1 Definieren, ändern, löschen 10.2.2 Parameter für Makros 10.3 Makros für bestimmte Programme 10.4 Makros speichern & laden 10.5 Tastenkombinationen für doskey 11 Shell Scripts (Batches) 11.1 11.2
11.3
11.4
11.5
11.6
11.7
11.8
8
Einführung Batchfiles 11.2.1 Aufbau 11.2.2 Syntax 11.2.3 Kommentare in Batches 11.2.4 Parameter 11.2.5 Ausführen von Shell Scripts Parameterchecks 11.3.1 Anzahl und Art der Parameter 11.3.2 Mehr als neun Parameter mit shift 11.3.3 Flexible Parameterprüfung Ablaufsteuerung 11.4.1 Verzweigungen 11.4.2 Schleifen 11.4.3 Unterroutinen 11.4.4 Shell Scripts beenden Selbstaufruf 11.5.1 Aufruf mit call %0 11.5.2 Aufruf mit CALL :label Synchronisation 11.6.1 Warten auf Prozesse 11.6.2 Kommunikation von Batches Selbstmodifikation 11.7.1 Grundlagen 11.7.2 Beispiel Call-Logging 11.7.3 Code und Daten kombinieren Interaktion mit dem Benutzer 11.8.1 Ein- und Ausgabe 11.8.2 Systeminformationen / Warten
16.4.11 dsrm 16.4.12 Passwörter per ldifde ändern 16.4.13 Gruppenmitglieder auflisten 16.5 Netzwerk 16.5.1 Netzwerkadresse berechnen 16.5.2 GetMAC für NT4 und W2K 16.5.3 Pingliste 16.5.4 Portliste aus netstat 16.6 Die Tools zum Buch 16.6.1 BinChk 16.6.2 DispChg 16.6.3 MocWall 16.6.4 XEcho 17 Referenz 17.1 17.2 17.3 17.4
Shell-Kommandos Windows 2003 Support Tools Windows 2003 Resource Kit Informationen zu einzelnen Befehlen 17.4.1 Dateiverwaltung 17.4.2 Netzwerk 17.4.3 ActiveDirectory 17.4.4 Systemverwaltung 17.4.5 Monitoring/Prozessverwaltung
Vorwort Microsoft Windows hat seinen Siegeszug nicht zuletzt der einfachen Bedienung über eine graphische Benutzeroberfläche zu verdanken. Diese Eigenschaft schätzen nicht nur Endbenutzer, sondern auch Administratoren, da sie durch die grafischen Werkzeuge viele Administrationsaufgaben intuitiv ohne Kenntnisse komplexer Befehle und Parameter realisieren können. Administratoren sind es aber auch, die oftmals die graphische Benutzeroberfläche von Windows als „KlickiBunti-Schnittstelle“ verfluchen, wenn wiederkehrende Aufgaben wie das Anlegen von Benutzerkonten zu Maus-Orgien werden. Die alten DOS-Befehle sind zwar auch heute in der Form der Eingabeaufforderung in Windows enthalten, doch hat es Microsoft lange versäumt, den Befehlsumfang entsprechend der ständigen Weiterentwicklung von Windows anzupassen. Seit Ende der 90er Jahre bietet Microsoft den Windows Script Host (WSH) als Basis für die befehlbasierte Administration an. Der WSH setzt jedoch eine Affinität zur (objektorientierten) Programmierung voraus, die viele WindowsAdministration nicht besitzen. Microsoft macht kein Geheimnis daraus, dass man im Zuge der Entwicklung des Windows Server 2003 einigen Unix-Administratoren über die Schulter geschaut hat, um Ideen für die Verbesserung des befehlsbasierten Administration zusammeln. Die Erkenntnisse aus dieser Studie manifestieren sich in einer Vielzahl neuer Kommandozeilenwerkzeuge im Windows Server 2003. Armin Hanisch greift in seinem Buch „Windows Server 2003 Shell Scripting“ die traditionellen Shell-Befehle ebenso wie die neuen Funktionen der Kommandozeile auf, um daraus ein umfassendes Bild für die Verwaltung von Windows durch Kommandozeile und Batchdateien zu zeichnen. Er dokumentiert nicht nur die in der Standardinstallation von Windows Server 2003 enthaltenen Werkzeuge, sondern auch die Windows Server 2003 Support Tools und die Windows Server 2003 Resource Kit Tools. Dabei hat Herr Hanisch auch mich überrascht mit vielen Funktionsdetails, die ich bisher nur mit dem WSH für möglich hielt. „Windows Server 2003 Shell Scripting“ ist ein sehr empfehlenswertes Buch für alle, die eine automatisierte Administration von Windows mit den vergleichsweise einfachen Shell-Befehlen und ohne umfassende Programmierkenntnisse anstreben. Herr Hanisch zeigt anschaulich die Möglichkeiten und auch die Grenzen der Shell-Befehle auf. Dr. Holger Schwichtenberg Softwarearchitekt, Buchautor und Dozent Essen, im August 2004
13
1 1.1
Einleitung
Warum dieses Buch?
Möglicherweise stehen Sie in diesem Moment in einer Buchhandlung und blättern gerade die ersten Seiten dieses Buches durch. Vielleicht fragen Sie sich: „Soll ich das Buch kaufen, vermittelt es mir Informationen, die mir helfen, Zeit zu sparen oder Arbeitsabläufe zu automatisieren“? Dieses Buch soll Ihnen helfen, Zeit bei der Arbeit mit Windows 200X zu sparen, ohne programmieren lernen zu müssen. Das vorliegende Buch entstand aus der täglichen Praxis bei meiner Tätigkeit als Consultant und Trainer im Bereich Windows-Server. In den offiziellen Trainingshandbüchern finden sich keine Hinweise, wie viel Arbeit sich ein Administrator durch effiziente Nutzung der Shell sparen kann. Hier finden Sie diese Hinweise; egal, ob Sie Dateien kopieren, Konten im ActiveDirectory bearbeiten oder Prozesse überwachen. Sie hätten gerne einige Beispiele, warum die Shell von Windows Ihr Freund ist? Überlegen Sie sich an dieser Stelle einmal, welche und wie viele GUI-Aktionen zur Lösung der Aufgabe nötig wären. 왘 Kopieren Sie alle .log- und .rpt-Dateien aus dem Stammverzeich-
nis der Rechner von 192.168.1.1 bis 192.168.1.20 in das Verzeichnis logs des Laufwerks C: Ihrer Maschine. 왘 Finden Sie alle exe-Dateien in system32, die eine gleichnamige
DLL besitzen. 왘 Welche Dateitypen hat der Internet Explorer auf Ihrem System für
sich registriert? 왘 Benennen Sie alle .jpg-Dateien im Ordner c:\fotos so um, dass
diese Dateien aufsteigend nummerisch umbenannt werden. 왘 Welche Dateien in Ihrem Verzeichnis source wurden im letzten
Monat geändert? Wie groß sind diese Dateien alle zusammen? (Die Antworten finden Sie übrigens im Anhang.)
1.2
Wer sollte dieses Buch lesen?
Wenn Sie als Administrator(in) im Bereich Windows (ab Windows 2000) tätig sind, werden Sie von diesem Buch profitieren. Es vermittelt Ihnen als erstes deutschsprachiges Werk einen Gesamtüberblick über die Möglichkeiten (und Grenzen) der Windows Shell. Sie erfahren, wie Sie die Arbeiten schneller erledigen und Aufgaben automati-
15
1 Einleitung
sieren. Im Gegensatz zum Windows Script Host müssen Sie nicht programmieren können, um die Möglichkeiten der Shell zu nutzen, die Lernkurve ist also viel flacher. Die zweite Gruppe, denen das Buch bei der Arbeit helfen wird, sind die so genannten „Power User“, seien es Entwickler oder Profis im Bereich Microsoft Office. Hier erfahren Sie, wie Sie Aufgaben im Bereich Dateimanagement schneller erledigen, eigene Befehle erstellen und Tausende von Mausklicks durch wiederholbare Shell Scripts sparen. Sie erhalten Hinweise zur Lösung von immer wieder anfallenden Problemstellungen und erfahren, welche zusätzlichen, freien Tools verfügbar sind. Sie programmieren nicht und administrieren hauptsächlich keine Windows-Server, sondern Unix, Linux, VMS oder andere? Dann erhalten Sie hier einen detaillierten Einblick für Umsteiger in die Windows Shell. Sie erfahren, welche Konzepte aus anderen Systemen übernommen wurden; wie und ob Sie Shell Scripts von Ihrem System nach Windows übernehmen können und welche Tools bereits für Sie verfügbar sind. Aber auch wenn Sie regelmäßig mehrere Systeme überwachen müssen, automatisierte Setups für mehr als ein Betriebssystem auf verschiedenen Hardware-Plattformen bereitstellen müssen, finden Sie hier Lösungen.
1.3
Buchgliederung
Dieses Buch ist in drei Abschnitte gegliedert. Im ersten Abschnitt stelle ich Ihnen die grundlegenden Konzepte der Shell vor. Dort erfahren Sie alles über die Themen Ein-/Ausgabeumleitung, Ablaufsteuerung, Prozessverwaltung. Makros und andere Bereiche. Im zweiten Abschnitt dreht sich alles um das Thema Shell Scripts (Batch Files), von den Grundlagen bis hin zu selbstmodifizierenden Scripts. Dieser Abschnitt enthält auch eine Reihe von Shell Scripts, die komplett dokumentiert sind und Aufgabenstellungen aus der Praxis lösen. Hier lernen Sie, wie an solche Probleme heranzugehen ist und welche Alternativmöglichkeiten der Lösung bestehen. Der dritte Teil ist eine Mischung aus Referenz und Tipps zu den einzelnen Kommandos der Windows Shell. Sie erhalten eine kompakte Liste aller Kommandos mit einer Zeile pro Befehl sowie zu ausgewählten Kommandos ausführlichere Erläuterungen und Hinweise zur optimalen Nutzung.
16
Hinweise zum Buch
1.4
Hinweise zum Buch
1.4.1
Die Windows-Versionen
Das Buch trägt zwar den Titel „Windows 2003 Shell Scripting“, einen Großteil der hier zu findenden Informationen können Sie ohne weiteres auch in Windows 2000 oder Windows XP anwenden. Im dritten Teil des Buches finden Sie eine Kurzliste der Befehle, aus der Sie entnehmen können, ab welcher Windows-Version ein Kommando zum Standardumfang der Shell gehört. Für einige komplexere Themen finden Sie in den jeweiligen Kapiteln auch eine Beschreibung, wie sich die gleiche Funktionalität auch vor Windows 2003 realisieren lässt. Viele Kommandos sind auch in den früheren Versionen schon über Updates, Resource Kits oder Entwicklungen von Drittherstellern verfügbar, die Basis des Buches bildet aber der Befehlssatz für die Shell, wie er seit Windows 2003 (in der Standardinstallation plus den Support Tools) verfügbar ist.
1.4.2
Schriftarten und Layout
Alle im Fließtext vorkommenden Befehle sind in einer eigenen Schriftart gesetzt. So können Sie Kommandos wie cls oder tracert leicht identifizieren. Einträge in Menüs wie DATEI / SPEICHERN oder EIGENSCHAFTEN werden ebenfalls besonders hervorgehoben. Sollten Sie Tastenkombinationen im Fließtext finden, sehen diese so aus: Drücken Sie (Entf) oder (Strg)+(A). Besonders wichtige Informationen oder potenziell gefährliche Befehle sind mit diesem Icon hervorgehoben.
So sind Hinweise und Bemerkungen zu einzelnen Befehlen oder für bestimmte Windows-Versionen gekennzeichnet Alle Listings und interaktiven Kommandos in diesem Buch sind so gesetzt, dass eine Listingzeile mit einer Zeilenschaltung abgeschlossen wird, wie das Beispiel unten zeigt. echo zeile 1 echo Zeile 2
Sollte eine Zeile länger sein als die Zeilenbreite des Buchlayouts erlaubt, dann finden Sie in der Marginalspalte einen Hinweis, dass es sich um eine logische Eingabezeile handelt.
17
1 Einleitung Eine Zeile
tasklist /fo list /fi "username ne NT AUTHORITY\*" | find /i "Image name:"
Bei längeren Auflistungen von Kommandos, die länger als eine Zeile sind, finden Sie den Hinweise im laufenden Text.
1.4.3
Syntaxkonventionen
Ein Kommando der Windows Shell besteht in der Regel aus dem Kommando selbst sowie einer mehr oder weniger langen Reihe möglicher Optionen und Parameter. Alle optionalen Angaben werden in eckige Klammern eingeschlossen: kommando [/S]
Das bedeutet, dass die Option /S auch weggelassen werden kann. Alle Platzhalter für Dateinamen, Suchstrings und andere Informationen werden durch einen symbolischen Namen in spitzen Klammern ersetzt. kommando
Das bedeutet die Angabe des Dateinamens nach dem Kommando. Ist die Angabe optional, wird der Platzhalter wie oben in eckige Klammern gesetzt: kommando [ ]
Alternativen sind durch einen senkrechten Strich voneinander getrennt: kommando [ /T "user" | "system" ]
Das bedeutet, dass Sie nach der Option /T entweder "user" oder "system" angeben müssen oder die Option komplett weglassen.
1.4.4
Stil und Anrede
Ich habe in diesem Buch versucht, die Windows Shell und ihre Möglichkeiten so zu beschreiben, dass Sie als Leser möglichst viel Nutzen davon haben. Das Thema selbst ist schon technisch genug, daher wollte ich nicht noch bei Stil und Anrede dafür sorgen, dass beim Lesen der Staub aufsteigt. Einige Anmerkungen und Kommentare geben nur meine persönliche Meinung wieder. So weit als möglich habe ich diese in Fußnoten gefasst, um den Lesefluss nicht stören. Die gerade aktuelle Mode, möglichst in jeder zweiten Zeile einen Marginaltext zu verfassen, habe ich aus Gründen der Lesbarkeit vermieden. Falls Sie also eine Randbemerkung oder ein Icon finden, ist es an dieser Stelle auch angebracht.
18
Feedback, Download der Beispiele
Üblicherweise benutze ich im Buch zwar die männliche Anrede, dies allerdings nur aus Gründen der Lesbarkeit. Dieses Buch ist nicht geschlechtsspezifisch. Einige der besten und kreativsten Leute im Bereich der Softwareentwicklung sind Frauen (fast alle meine Assembler-Kenntnisse stammen von einer netten Studienkollegin). Vielleicht würde manche Software und mancher Server besser laufen, wenn wir einen höheren Frauenanteil in der Branche hätten. Ich denke aber nicht, das ein Konstrukt wie „der/die Administrator(In)“ in Büchern besser lesbar ist.
1.4.5
Weblinks, Warenzeichen
Die in diesen Unterlagen enthaltenen Daten und Angaben, einschließlich URLs und anderer Verweise, können sich mit vorheriger Ankündigung ändern. Auf die Verfügbarkeit externer Weblinks sowie die dort bereitgestellten Inhalte hat der Autor keinen Einfluss und gibt diese Daten ohne Gewähr oder Haftung und rein zu Zwecken der Information an. Alle in diesen Unterlagen aufgeführten Produkt- und Firmennamen sind unter Umständen Marken oder geschützte Zeichen der einzelnen Firmen. Die Verwendung von Namen ohne eine entsprechende Kennzeichnung bedeutet nicht, dass diese Namen und Bezeichnungen frei von Schutzrechten sind. Microsoft, MS-DOS, Windows, Windows NT, ActiveDirectory, Windows 2000, Windows XP, Windows 2003, VBScript und Windows Script Host sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern.
1.5
Feedback, Download der Beispiele
Ich freue mich über Feedback zu diesem Buch, Fehlerhinweise und Vorschläge für Ergänzungen. Bitte senden Sie diese per E-Mail an die Adresse [email protected] oder per normaler Post an den Verlag. Die Beispiele und Tools, die in diesem Buch verwendet werden, finden Sie auf der Homepage des Autors. Die URL für die Seiten zum Buch lautet http://www.ArminHanisch.de/books/shellbook.html. Haben Sie bitte Verständnis dafür, dass ich nicht jedes zweizeilige Kommando in das Archiv gepackt habe; der Download bedeutete mehr Arbeit als das eigene Eintippen. Alle Beispielcodes und die Tools sind Freeware. Die Scripts dürfen von Ihnen benutzt, verändert und an eigene Anforderungen angepasst werden. Für alle Scripts und Programme gilt, dass trotz sorgfältiger Prüfung keine Garantie für die
19
1 Einleitung
Funktion in einer bestimmten Systemkonfiguration übernommen werden kann. Die Benutzung erfolgt auf Ihr eigenes Risiko. Alles, was ich garantiere, ist die Tatsache, dass die Programme und Scripts Platz auf Ihrem Datenträger belegen werden. Insbesondere weise ich darauf hin, dass diese Shell Scripts und die Programme für die Benutzung durch erfahrene Anwender und Administratoren gedacht sind. Ich übernehme keine Verantwortung für Schäden oder Datenverlust, die aus dem unsachgemäßen Einsatz von Programmen, Scripts und Listings aus diesem Buch resultieren.
1.6
Danksagungen
Ohne meinen Vater Winfried würde es dieses Buch sicher nicht geben. Er hat mir die Faszination des Amateurfunks gezeigt und mich 1979 auf die Ham-Radio am Bodensee mitgenommen. Dort war dann ein PET 2001 interessanter als alle Kurzwellengeräte. Dies war der Anfang von allem. Kein Buch wird vom Autor alleine geschrieben. Auch zum Gelingen dieses Buches haben viele Leute beigetragen, die mich unterstützten, mit Ideen versorgten, antrieben und ihre Meinung und wertvolle Anregungen einfließen ließen. Ein besonderes Dankeschön allen Seminarteilnehmern meiner Shell-Kurse, die mich immer wieder aufforderten, meine Erfahrungen als Buch zu veröffentlichen. Viele Anregungen stammen aus den Kursen und aus Support Scripts zu Kundenprojekten. Besonders bedanken möchte ich mich beim Verlagsteam von AddisonWesley, insbesondere bei Sylvia Hasselbach für die professionelle Betreuung und bei Sandra Gottmann für das Korrektorat. Dank auch an die „Beta-Leser“, Freunde und Kollegen, die trotz Familie, Projekten, Gartenarbeit und eigenen Verpflichtungen Zeit fanden, das Manuskript Probe zu lesen. Vor allem haben Michael Schad, Alexander Tihme und Heinz Fischer zum Gelingen dieses Buches beigetragen. Besonders bedanken möchte ich mich auch bei Dr. Holger Schwichtenberg für das Fachlektorat und die einleitenden Worte zum Buch. Den meisten Dank schulde ich meiner Frau Andrea, die auch dieses Mal wieder mit großer Geduld, Espresso und liebevollen Nackenmassagen dafür gesorgt hat, dass ich nie die Lust am Schreiben verloren habe.
20
2 2.1
Shell Basics
Was bedeutet Shell?
Der Ausdruck shell stammt aus den 70er-Jahren, als die ersten Time Sharing-Rechner erschienen, an denen interaktiv gearbeitet werden konnte. Er trifft mit seiner wörtlichen Übersetzung „Muschel“ den Kern der Sache recht genau. Eine Shell ist das Programm, über das ein Benutzer mit dem System kommuniziert. Ein Synonym im Deutschen ist Kommandoprozessor oder Kommandointerpreter. Da sich ein solches Programm logisch gesehen „über“ bzw. „um“ das System legt und wie eine Muschel die Perle umschließt, wird das Programm zum Starten und Verwalten von Programmen, dem Dateimanagement und anderen Sachen eben Shell genannt. Das Programm, das unter Windows beim Start einer Benutzersitzung automatisch geladen wird, ist der Windows Explorer. Dieses GUIProgramm bietet aber keine Möglichkeit der direkten Kommandoeingabe. Obwohl theoretisch auch der Kommandoprozessor cmd.exe direkt nach dem Login geladen werden könnte, ist dies (noch) nicht praktikabel, da unter Windows einige Funktionen nur über das GUI erreichbar sind. Daher wird cmd.exe wohl in den meisten Fällen nach dem Login durch den Benutzer oder über die Autostart-Funktion aktiviert werden. Wenn in diesem Buch der Ausdruck „Shell“ verwendet wird, ist also immer cmd.exe gemeint. Im Gegensatz zu Unix wird bei Windows nach der Installation keine alternative Shell mitgeliefert, die anstelle von cmd.exe benutzt werden könnte. Es gibt aber eine ganze Reihe von Herstellern, die Programme zu diesem Zweck anbieten oder Shells aus der Unix-Welt nach Windows portiert haben. Alle Beispiele und Scripts in diesem Buch können mit der Standard-Shell cmd.exe durchgeführt werden. Für eine Arbeitszeit sparende Systemverwaltung über die Kommandozeile sind also keine besonderen Installationen notwendig.
2.2
Grundlagen
Warum die Shell nutzen? Heute sind moderne Betriebssysteme doch intuitiv über eine grafische Oberfläche bedienbar, niemand muss sich mehr irgendwelche Optionen merken, alles wird einfach per Mausklick an die richtige Stelle geschoben – oder?
21
2 Shell Basics
Für den Anwender mag dies zutreffen. Es gibt allerdings zwei große Gruppen, die heute und auch in Zukunft komplexere Kommandos ausführen müssen: Administratoren und Softwareentwickler. Sie werden täglich damit konfrontiert, schnell und flexibel spezialisierte Operationen mit Prozessen und Dateien durchführen zu müssen, einer Aufgabe, für die eine grafische Oberfläche nur bedingt geeignet ist. Warum dann cmd? Sicherlich existiert mittlerweile eine ganze Reihe guter Portierungen von Unix-Software für die Windows-Plattform, gerade auch im ToolBereich. Als Beispiel unter vielen sei die cygwin-Distribution genannt, die neben der von Unix bekannten bash („bourne again shell“) weit über 100 Unix-Kommandos mitbringt. Es existiert aber auch eine Reihe nativer Win32-Ports der GNU-Shell Utilities, die außer der Visual C++Laufzeitbibliothek keine weitere Installation benötigen und somit auf einem Client ohne Installation über eine Serverfreigabe aufgerufen werden können. Dennoch ist für die meisten Aufgabenstellungen heute keine weitere Software mehr nötig als die Windows Shell cmd.exe und evtl. noch die Kommandos aus den Support-Tools und dem Resource Kit. Gerade bei Windows 2003 steht also einer kommandozeilenorientierten Verwaltung der Maschine immer weniger im Weg.
2.3
Shell Scripts oder Batch Files?
Nicht alle Aufgaben können mit einem einzigen Kommando auf der Befehlszeile erledigt werden. Daher ist wie auch bei Windows eine Möglichkeit vorhanden, mehrere Kommandos in einer Datei zusammenzufassen und diese zusammen ablaufen zu lassen. Während die Unix-Leute bei dieser Möglichkeit von so genannten Shell Scripts sprechen, hat sich in Windows der Ausdruck Batch Files eingebürgert. Gemeint ist in beiden Fällen das Gleiche: eine Reihe von Kommandos, die in einer Datei gespeichert ist, die durch die Angabe ihres Dateinamens gestartet und ausgeführt wird. Auch hier im Buch wird Shell Script und Batch File synonym verwendet. Weitere Begriffe mit der gleichen Bedeutung sind Stapeldatei, Shell-Prozedur, Batch oder einfach (und ungenau) nur Script. Der Ausdruck Windows-Script oder VB-Script bezeichnet etwas anderes, nämlich eine Befehlsdatei für den Windows Script Host. Mit mehreren Sprachen und mehr Funktionen für Systemaufrufe ausgestattet, spricht der WSH eher die Programmierer unter den Administratoren an. Wie überall in der IT wird mehr Funktionalität mit einer steileren Lernkurve erkauft, bietet dann aber auch mehr Möglichkeiten der Problemlösung. Daher ist die Frage nicht entweder Shell Scripts oder Windows Script Host, sondern beide ergänzen sich ideal. So weit für den Einstieg, in Kapitel 15 („Die Shell und der WSH“) gehe ich im Detail auf die Frage „Shell, WSH oder beide?“ ein.
22
Shell starten
2.4
Shell starten
Für das von cmd.exe bereitgestellte Befehlsfenster existiert eine ganze Reihe synonymer Bezeichnungen. Ganz gleich, ob in der Literatur von der Eingabeaufforderung, dem Shell-Fenster, dem DOS-Fenster, dem englischen console window oder dem Konsolenfenster die Rede ist – alle bezeichnen das gleiche Objekt. Ich habe mich im Buch bemüht, möglichst immer den Begriff Shell-Fenster bzw. Shell zu verwenden. Sollte trotzdem einmal eine Bezeichnung aus der obigen Liste auftauchen, wissen Sie nun, dass es mein Fehler war und ich dennoch von cmd.exe und seinem Fenster schreibe. Jeder Administrator, Entwickler oder Power User sollte mindestens ein Shell-Fenster offen haben, da viele Sachen damit leichter von der Hand gehen. Beim Starten einer Instanz der Shell haben Sie eine ganze Reihe von Möglichkeiten, Optionen anzugeben, welche das Verhalten von cmd.exe genauer steuern. Eine der einfachsten Möglichkeiten, an ein Shell-Fenster zu kommen, ist die Benutzung der Tastenkombination (ÿ)+(R) (oder dem Klick auf die Schaltfläche START und dann AUSFÜHREN). In der erscheinenden Dialogfläche geben Sie cmd ein, und drücken Sie die Eingabetaste. Als Ergebnis erhalten Sie ein Shell-Fenster.
2.4.1
Die Befehlserweiterungen
Viele Befehle der Shell existieren bereits seit der IT-Steinzeit, also seit MS-DOS 2.x, und dürfen daher aus Gründen der Abwärtskompatibilität eigentlich nicht in ihrer Funktion verändert werden. Aus diesem Grund hat Microsoft das Konzept der erweiterten Möglichkeiten für den Befehlsprozessor eingeführt. Dies sind Aktualisierungen an vielen Kommandos, die per Default bei der Shell aktiviert sind (und ohne guten Grund auch nicht deaktiviert werden sollten, da Sie viel an Funktionalität einbüßen). Da die Shell mit jeder Windows-Version seit Windows NT 4 deutlich erweitert wurde, existieren verschiedene Versionen dieser Erweiterungen. Bei NT4 war dies die Version 1, bei Windows 2000 die Version 2 usw., daher existiert eine automatisch belegte Umgebungsvariable (s. Abschnitt 6.1.3), mit der festgestellt werden kann, welche Version der Befehlserweiterungen aktiv sind. Sind die Befehlserweiterungen der Shell nicht aktiv, existiert diese Variable nicht. Für Windows 2000 liefert die Abfrage folgende Ausgabe: c:\tmp>echo %CMDEXTVERSION% 2
Für sämtlichen Code in diesem Buch wird davon ausgegangen, dass die Befehlserweiterungen wie nach einer Standardinstallation aktiviert sind.
23
2 Shell Basics
2.5
Arbeiten mit der Kommandozeile
2.5.1
Befehle eingeben
Was Sie nach dem Start des Shell-Fensters vor sich haben, sieht ungefähr so aus wie die folgende Abbildung (grafisch gesehen eher langweilig): Abbildung 2.1: Das Shell-Fenster in seiner ganzen Pracht
Die Angabe vor dem blinkenden Cursor ist die Systemmeldung (engl. prompt), die ohne Veränderungen einfach aus dem aktuellen Verzeichnis, gefolgt von einem Größer-Zeichen, besteht. Im nächsten Kapitel erfahren Sie, wie Sie diese Meldung verändern können. Nach dem Größer-Zeichen beginnt die Eingabezeile für Kommandos. Diese Kommandos können ab Windows 2000 bis zu etwa 2000 Zeichen lang sein, so dass auch längere Pfadangaben oder komplexe Kommandos kein Problem darstellen. Wollen Sie ein eingegebenes Kommando ausführen lassen, drücken Sie einfach die Eingabetaste. Das Kommando wird ausgeführt, etwaige Ausgabe werden im Shell-Fenster ausgegeben, danach erscheint wieder die Systemmeldung, und Sie können das nächste Kommando eingeben. Die folgende Bildschirmausgabe zeigt dies (alle Eingaben sind fett markiert und wurden mit der Eingabetaste (Enter) abgeschlossen):
24
Arbeiten mit der Kommandozeile c:\tmp> time /t 18:31 c:\tmp> dir test.* Datenträger in Laufwerk C: ist Lokaler Datenträger Datenträgernummer: 8C11-3AFE Verzeichnis von C:\tmp 24.02.2004 20.12.2003
Das Kommando exit beendet übrigens die aktuelle Shell und schließt Shell verlassen das Fenster wieder. Alle Eingaben müssen in einer logischen Zeile erfolgen. Dies bedeutet, dass eine Eingabe zwar durchaus länger werden kann als die Breite des Fenster und dann auch in die nächste Zeile umbrochen wird, trotzdem aber als eine Eingabezeile definiert wird. Während der Eingabe haben Sie natürlich eine Reihe von Bearbeitungsmöglichkeiten, die über das simple Verbessern mit Hilfe der Korrekturtaste weit hinausgehen. Im Abschnitt 3.0.1 erfahren Sie mehr über die verfügbaren Tastenkombinationen. Ein gerade laufendes Kommando kann in seiner Ausführung in der Abbrechen Regel abgebrochen werden. Angenommen, Sie bemerken, dass Sie die IP-Adresse für einen ping falsch angegeben haben. Mit Hilfe der Tastenkombination (Strg)+(C) oder (Strg)+(UntBr) (bzw. (Ctrl)+(Break) bei US-Tastaturen) wird das laufende Kommando beendet. In der Ausgabe erscheint als Hinweis auf den Abbruch die Zeichenfolge ^C.
2.5.2
Kommandotypen
Aus historischen Gründen (und auch im Sinne der Erweiterbarkeit) ist die Shell kein monolithischer Block. Nicht die gesamte Funktionalität steckt in cmd.exe, sondern nur die so genannten internen Kommandos. Dies sind Befehle, für die keine ausführbare Datei vorliegt, sondern die tatsächlich in cmd.exe enthalten sind (copy, dir, cd etc.). Diese Befehle umfassen die wichtigsten Kommandos für die tägliche Dateiverwaltung des Systems1. Als externe Kommandos werden alle Befehle der Shell bezeichnet, die auch in Form von Programmen oder Skripten (meist im System321
Seinen Ursprung hat dies in uralten DOS-Zeiten. Der Befehlsinterpreter sollte damals möglichst viel Funktionalität ohne Nachladen externer Programme von der Diskette beinhalten, aber auch nicht zu viel Speicher für selten benötigte Kommandos belegen.
25
2 Shell Basics
Verzeichnis) vorliegen. Dazu gehört die Mehrzahl der Befehle, von Kleinigkeiten wie find bis hin zu größeren und komplexeren Kommandos wie dsquery. 2
Die allgemeine Syntax eines Kommandos folgt prinzipiell dem unten stehenden Schema: <parameter>
Prinzipiell deshalb, weil im gesamten Windows-Bereich wohl kaum etwas Inkonsistenteres existiert als die Syntax der Windows Shell. Dies hat seinen Ursprung in der Entwicklung der Shell, weshalb ich an dieser Stelle etwas weiter ausholen möchte. Falls Sie also gerade keine ruhigen fünf Minuten haben, blättern Sie weiter, und lesen Sie diesen Abschnitt später … Auch die Shell von Windows 2003 hat ihren Ursprung in der Kommandozeile von MS-DOS. Und für die Shell von MS-DOS hatte man sich an CP/M orientiert. Da MS-DOS zu dieser Zeit (Version 1.x) noch kein hierarchisches Dateisystem hatte (jawohl, keine Unterverzeichnisse!), benötigte man auch kein Zeichen als Trennzeichen für einen Dateipfad. Was aber benötigt wurde, war ein Zeichen, mit dem eine Option gekennzeichnet wurde (um beispielsweise bei der Ausgabe einer Datei anzugeben, dass die Ausgabe nummeriert erfolgen soll). Die heute in Windows so übliche Variante mit dem Vorwärtsschrägstrich („/“) ist zu dieser Zeit entstanden. In CP/M gab es kein einheitliches Zeichen für eine Befehlsoption. Einige Kommandos nutzten das Dollarzeichen, einige andere gaben die Optionen in eckigen Klammern an, und einige benutzten den Schrägstrich2. Die damals von Microsoft für CP/M produzierten Entwicklertools nutzten allerdings alle den Schrägstrich für die Optionskennzeichnung. Es folgt der Auftritt von MS-DOS 2.x und eines hierarchischen Dateisystems mit Unterverzeichnissen. Das Thema Schrägstrich wie bei Unix war mit der Einführung dieses Zeichens für die Optionen vom Tisch. Blieb also nur ein anderes Zeichen. Dies war, weil auf US-Tastaturen leicht erreichbar, der Backslash („\“). Bis zu MS-DOS 5 existierte allerdings in der Datei config.sys ein kaum bekannter Schalter mit dem Namen SWITCHAR, der das Zeichen für Optionen (engl. switches) definierte. Eine weitere Auswirkung dieser Entscheidungen war die Wahl des Zeichens „^“ als Escape-Zeichen für die Kommandozeile (wie in echo 4 ^> 2), da der Backslash ja schon vergeben war.
2
26
Etwas Stöbern in meinen fossilen EDV-Büchern sowie bei Google brachten die Erkenntnis, dass Digital Research bei CP/M damals von TOPS, einem Betriebssystem für die PDP-Rechner, von DEC inspiriert wurde (und VMS benutzt heute noch den Schrägstrich für Optionen).
Hilfe erhalten
Mit zunehmender Funktionalität im Netzwerkbereich wurden einige Kommandos aus diesem Bereich nach DOS (und auch Windows) portiert, beispielsweise das altbekannte ping oder auch tracert, arp und alle anderen. Diese Kommandos stammen aber historisch aus dem BSD-Unix von Berkeley. Dort war schon immer das Minuszeichen die Kennung für Optionen (der Schrägstrich trennt dort ja einzelne Verzeichnisse). Aus diesem Grund verwenden alle TCP/IP-orientierten Kommandos auch in Windows 2003 noch das Minuszeichen für die Optionen. Auch die Reihenfolge Parameter und Option orientiert sich für die Netzwerkbefehle an Unix: Zuerst kommen eventuelle Optionen, danach Parameter wie Dateiname oder IP-Adresse. In Windows werden zuerst die Parameter angegeben, gefolgt von eventuellen Optionen. Um die Verwirrung komplett zu machen, hat Microsoft bei einigen neuen Shell-Kommandos begonnen, ebenfalls die Optionen vor die Parameter zu stellen. Als Resümee dieses Exkurses bleibt nur die Tatsache, dass es eben kein in sich logisches Schema für den Aufbau eines Windows Shell-Kommandos gibt und Sie im Zweifelsfall /? und, wenn dies nicht klappt, -? versuchen sollten.
2.5.3
Behandlung von Sonderzeichen
Wann immer Sie ein Zeichen mit einer besonderen Bedeutung als reinen Text eingeben möchten, müssen Sie dieses Zeichen entsprechend kennzeichnen (im Fachjargon quoten). Dafür wird ein ebenfalls ein besonderes Zeichen (ein so genanntes Escape-Zeichen) benutzt. Das Escape-Zeichen für die Shell ist das Zeichen ^ (zur Geschichte siehe den letzten Abschnitt), z.B. bei ECHO ^. So lassen sich sogar XML-Dateien notfalls per Shell erzeugen … Diese Kennzeichnung ist später auch bei allen anderen Operatoren und vor allem beim Backquoting (Abschnitt 9.2.8) notwendig. Falls Sie ein „^“ ausgeben wollen, verdoppeln Sie das Zeichen einfach.
2.6
Hilfe erhalten
2.6.1
Hilfe zu einzelnen Befehlen
Mittlerweise hat Microsoft versucht, die Kommandos der Shell so weit wie möglich zu vereinheitlichen. Dies bedeutet auch, dass nun für jedes Kommando eine Beschreibung der Syntax und ein kleiner Hilfetext abrufbar ist. Dazu rufen Sie das entsprechende Kommando mit der Option /? auf.
27
2 Shell Basics
Einzige Ausnahme: die Befehle aus dem Netzwerkbereich, die historisch alle der Unix-Welt entstammen. Hier ist das Optionszeichen das Minuszeichen, daher gilt für ping und Konsorten der Aufruf mit -?.
2.6.2
Das Kommando help
In der Shell existiert ein Kommando mit dem Namen help, dass Sie entweder ohne Parameter oder mit dem Namen eines Kommandos aufrufen können. Ohne Parameter erhalten Sie eine lange Liste aller internen Kommandos der Shell. Geben Sie ein Kommando aus dieser Liste als Parameter an, erhalten Sie die Online-Hilfe zu diesem Kommando. Toll, wo ist dann das Problem? Ganz einfach: help gibt eine unvollständige Liste der Kommandos aus, nicht alle verfügbaren Befehle für die Shell sind dort enthalten. Insbesondere fehlen die meisten Netzwerkkommandos (von ping über nbtstat bis arp). Daher können Sie sich für eine Liste der Kommandozeilenprogramme nicht allein auf help verlassen.
2.6.3
Die Online-Hilfe
Die Online-Hilfe aus dem Help and Support Center enthält nicht nur eine vollständige Auflistung aller Befehle, die für die Shell zur Verfügung stehen, sondern auch Hinweise, Erläuterungen und Beispiele. Alle Unix-Umsteiger, die an man gewohnt sind, müssen hier also doch auf eine GUI-Anwendung umsteigen … Im Gegensatz zur landläufigen Meinung ist diese Hilfe zur Kommandozeile wesentlich besser als ihr Ruf. Sie enthält neben der reinen Referenz auch grundlegende Informationen und viele Beispiele sowie versteckte Tipps, die wirklich Zeit sparen. Lassen Sie die Hilfe also nicht einfach links liegen.
2.7
Verzeichniswechseln
Bei der Arbeit mit dem Shell-Fenster beziehen sich alle Kommandos, die Sie ohne Pfad eingeben, auf das aktuelle Verzeichnis. Dies ist die in der Systemmeldung angegebene Kombination aus Laufwerk und Pfad. Aus diesem Grund ist dieses auch in der Systemmeldung enthalten. Möchten Sie in einem anderen Verzeichnis weiterarbeiten, müssen Sie erst dorthin wechseln. Dies geschieht entweder über die Eingabe des Laufwerksbuchstabens, gefolgt von einem Doppelpunkt und der Eingabetaste, oder über eines der unten erläuterten Kommandos zum Wechsel des aktuellen Verzeichnisses.
28
Verzeichniswechseln
Lange Dateinamen, die Leerzeichen enthalten, müssen bei Kommandos mit der Shell in doppelte Anführungszeichen eingeschlossen werden, da sonst der Dateiname nicht korrekt erkannt wird. Schließlich ist das Leerzeichen für die Shell auch eines der Trennzeichen für Befehle und Optionen.
2.7.1
cd (chdir)
Das Kommando cd (für change directory) wechselt in der Shell das aktuelle Verzeichnis zum angegebenen Pfad. Wird kein Pfad angegeben, so erhalten Sie als Ausgabe das aktuelle Verzeichnis angezeigt. Es gibt für cd (und einige wenige andere Kommandos) einen Sonderfall bei der Behandlung von langen Dateinamen mit Leerzeichen. Diese müssen bei aktivierten Befehlserweiterungen (siehe Abschnitt 2.4.1) nicht mit doppelten Anführungszeichen umschlossen sein, da cd alle Zeichen bis zum Ende der logischen Eingabezeile als Pfadangabe auffasst. Geben Sie trotzdem die Anführungszeichen immer an, dann müssen Sie sich keine Ausnahmen merken. cd c:\tmp cd "c:\program files\win32\drivers\etc“
Falls das aktuelle Laufwerk ein anderes ist als das im cd-Kommando angegebene, erfolgt nur der Wechsel des Pfades. Das aktuelle Laufwerk bleibt das gerade eingestellte Laufwerk, wie der folgende Konsolenmitschnitt zeigt: H:\>c: C:\tmp>cd h:\buch\samples C:\tmp> h: H:\Buch\Samples
Wie Sie erkennen, bleibt nach einem cd das aktuelle Laufwerk auf C: eingestellt.
2.7.2
pushd/popd
Die beiden Kommandos pushd und popd ersparen beim häufigen Wechsel von Verzeichnissen eine Menge Arbeit. Der erste Befehl wechselt (auch über Laufwerksgrenzen) zum angegebenen Verzeichnis, legt aber vorher das aktuelle Verzeichnis in einem Stapel ab. Nach der Eingabe von popd wird dieses Verzeichnis wieder vom Stapel gelesen und als aktuelles Verzeichnis eingestellt. Im Gegensatz zu cd wird hier bei der Angabe eines Pfads mit einem anderen Laufwerk auch das aktuelle Laufwerk gewechselt.
29
2 Shell Basics
2.7.3
Aktuelles Verzeichnis feststellen
Falls Sie die Systemmeldung mit Hilfe von prompt (s. Abschnitt 3.3.2) umgestellt haben, können Sie trotzdem jederzeit das aktuelle Laufwerk und Verzeichnis feststellen. Geben Sie dazu einfach das Kommando cd ohne Parameter ein, und Sie erhalten als Ausgabe den aktuellen Pfad der Shell. Bei aktivierten Befehlserweiterungen existiert auch eine dynamische Umgebungsvariable CD, die jeweils das aktuelle Arbeitsverzeichnis der Shell enthält. Geben Sie zum Ausprobieren einfach das folgende Kommando ein. echo Aktuelles Verzeichnis = %CD%
2.8
Wo ist command.com?
Einige Umsteiger von Windows 98 oder noch älteren Systemen werden sich vielleicht fragen, wieso cmd.exe als Shell bezeichnet wird, wenn doch seit DOS-Zeiten der Kommandointerpreter command.com war. Dieses Programm existiert auch noch, ist aber nur noch aus Gründen der Abwärtskompatibilität für alte 16-Bit-Anwendungen vorhanden. Wenn Sie command aufrufen, wird eine virtuelle DOS-Maschine gestartet (der Prozess mit dem Namen ntvdm.exe), die anschließend command.com lädt. Jeder eingegebene Befehl wird aber intern sofort wieder über eine cmd-Shell abgearbeitet. Falls Sie das selbst sehen möchten, müssen Sie nur die Prozesshierarchie ansehen. Dazu benutze ich das Programm tlist aus den Support Tools für Windows 2000 (mehr Informationen dazu erhalten Sie beim Thema „Prozessverwaltung mit der Shell“ ab Seite 82) und lasse mir mit tlist -t die Prozesshierarchie ausgeben. Nachfolgend der relevante Teil der Bildschirmausgabe: CMD.EXE (1136) C:\WINNT\system32\cmd.exe NTVDM.EXE (660) C:\WINNT\system32\command.com CMD.EXE (1536) tlist.exe (316)
Wie Sie erkennen, ist cmd.exe mit der Prozess-ID 1136 die StandardShell. Aus dieser habe ich command.com gestartet, der aber unter der Regie des 16-Bit-Subsystems läuft (ntvdm.exe mit der PID 660). Diese startet für die Abarbeitung der in ihr gestarteten Kommandos (hier unser tlist) wieder eine Instanz von cmd.exe, diesmal mit der PID 1536. Sie sehen also, es gibt (außer für alte Applikationen, die den Namen fest einprogrammiert haben) keinen Grund mehr für command.com3. 3
30
Eine „anständig“ programmierte Anwendung hatte sowieso nie den Aufruf von command.com fest einprogrammiert, sondern hat bereits zu Zeiten von MS-DOS die Umgebungsvariable comspec abgefragt, die immer den kompletten Pfad zur aktuellen Shell des Systems enthält.
3
Shell-Konfiguration
Dieses Kapitel beschreibt die Konfiguration der Shell zur Laufzeit und über Einstellungen und die Registry. Mit diesen Informationen können Sie die Shell optimal an die eigenen Wünsche anpassen. Ich habe dieses Kapitel so weit vorne angeordnet, da eine gut konfigurierte Shell die Arbeit leichter macht. Außerdem sind die Tastenkombinationen und Dinge wie die automatische Vervollständigung von Dateinamen notwendiges Handwerkszeug für die Arbeit mit der Kommandozeile.
3.0.1
Tastenkombinationen
Bei der Bearbeitung von Befehlszeile bei konsolenbasierten Programmen4 steht Ihnen eine ganze Reihe von Bearbeitungsmöglichkeiten zur Verfügung, die über simples rechts oder links mit den Cursortasten hinausgehen. Viele der weiter unten aufgeführten Kombinationen machen aber erst Sinn, wenn Sie sich mit dem Thema doskey und Makros (s. Kapitel 10) beschäftigt haben. Sollten Sie daher zur einen oder anderen Tastenkombination Fragen haben, blättern Sie ruhig vor und lesen dort nach. Die folgende Tabelle zeigt alle Tastenkombinationen, die für die Kommandozeile unterstützt werden. Tastenkombination Bedeutung (½)
Ein Kommando zurück im Stapel
(¼)
Ein Kommando vor im Stapel
(æ)
Ein Zeichen links
(Æ)
Ein Zeichen rechts
(CTRL)-links
Ein Wort links
(CTRL)-rechts
Ein Wort rechts
(CTRL)-Home
Löscht vom Cursor bis zum Zeilenanfang
(CTRL)-End
Löscht vom Cursor bis zum Zeilenende
(ESC)
Löscht die gesamte Eingabezeile
(F1)
Wiederholt die letzte Eingabe Zeichen für Zeichen
(F2)
Kopiert letzte Eingabe bis zum eingegebenen Zeichen
4
Tabelle 3.1: Tastenkombinationen für die Shell
Also nicht nur cmd.exe, sondern auch Programme wie ftp, nslookup oder andere Programme, die über die Standardeingabe bedient werden.
31
3 Shell-Konfiguration
Tastenkombination Bedeutung (F3)
Zeigt die letzte Kommandozeile wieder an
(F4)
Löscht vom Cursor bis zum angegebenen Zeichen
(F5)
Kopiert den aktuellen Befehlspuffer in die Eingabezeile
(F6)
Fügt ein Dateiendezeichen ein ((Strg)+(Z))
(F7)
Zeigt alle gespeicherten Befehle an. Wählen Sie mit (½) und (¼) den gewünschten Befehl aus, und drücken Sie zum Ausführen des Befehls die (¢). Sie können auch die vor dem Befehl stehende Nummer zusammen mit der Taste F9 verwenden.
(ALT)+(F7)
Löscht alle gespeicherten Befehle
(F8)
Zeigt alle Befehle im Stapel an, die mit den eingegebenen Zeichen beginnen
(F9)
Fordert Sie zur Eingabe einer Befehlsnummer auf und zeigt dann den Befehl an, der mit der angegebenen Nummer verknüpft ist. Mit der (¢) können Sie den Befehl ausführen. Um alle Nummern und die entsprechenden Befehle anzuzeigen, drücken Sie (F7).
(ALT)+(F10)
Löscht alle Makrodefinitionen
(Einfg)/(Ins)
Schaltet zwischen Einfügen und Überschreiben um
(Entf)/(Del)
Löscht das Zeichen rechts vom Cursor
(Œ)
Löscht das Zeichen links vom Cursor
3.0.2
Startparameter
Beim Start der Shell cmd.exe sind folgende Optionen möglich. Tabelle 3.2: Startoptionen der Shell
32
Option
Auswirkung
/C
Führt aus und beendet dann die Shell
/K
Führt aus und endet dann nicht
/S
Bestimmt Behandlung von Zeichenfolgen nach /C oder /K
/Q
Schaltet die Befehlsanzeige aus
/D
Deaktiviert eventuelle AutoRun-Befehle aus der Registrierung
/A
Ausgabe in eine Pipe oder Datei im ANSI-Format
/U
Ausgabe in eine Pipe oder Datei im Unicode-Format
Wo ist command.com?
Option
Auswirkung
/T:fg
Legt die Hinter-/Vordergrundfarben beim Start fest
/E:ON
Aktiviert die Befehlserweiterungen (Default)
/E:OFF
Deaktiviert die Befehlserweiterungen
/F:ON
Aktiviert die Ergänzung von Datei- und Verzeichnisnamen (s. )
/F:OFF
Deaktiviert die Ergänzung von Datei- und Verzeichnisnamen
/V:ON
Aktiviert verzögerte Erweiterung von Variablen (s. )
/V:OFF
Deaktiviert die verzögerte Erweiterung von Variablen (Default)
Die Option, die Sie wahrscheinlich am häufigsten benutzen werden, ist die Option /C . Erst durch diese Option lassen sich interne5 Shell-Kommandos wie Kopieroperationen auch von anderen Prozessen aus aufrufen. Als Beispiel soll in einer anderen Anwendung eine Datei von einem Share eines anderen Rechners kopiert werden. Falls Sie (als Entwickler) das für eine triviale Aufgabe halten, hier der Hintergrund aus der Praxis: Beim Microsoft SQL Server ist vor der Version 2000 kein Backup einer Datenbank auf einen UNC-Pfad möglich (wird von MS nicht unterstützt)6. Ebenso existiert vor dem SQL Server 2000 kein Assistent für das sog. Log Shipping (den Transport und die Anwendung des Transaktionsprotokolls auf einen entfernten Standby-Rechner). Bleibt also nur ein regelmäßiger Job, der das Backup erledigt, und ein anderer regelmäßiger Job auf dem StandbyServer, der die Sachen abholt7. Die Erstellung einer Routine zum binären Kopieren von Dateien in SQL ist nicht praktikabel. Die Job-Engine von SQL kann aber auch Kommandos der Shell oder andere Programme starten. Damit ist das Problem gelöst: cmd.exe /C "copy \\server\share\20040520-145320.bkup c:\logs"
Eine Zeile
Wenn Sie ganz sicher sein wollen, können Sie den direkten Aufruf von cmd.exe auch durch ein %comspec% ersetzen. Falls die Software eine 5 6
7
Zur Unterscheidung von internen und externen Kommandos siehe den Abschnitt 2.5.2. Nur der Vollständigkeit halber und weil mir Ihre Daten am Herzen liegen: Auch beim SQL-Server 2000 würde ich keinen Backup auf einen UNC-Pfad durchführen, sondern lokal sichern und dann auf den UNC-Pfad kopieren. Warum? Bei Netzwerkfehlern haben Sie mit der ersten Methode eine kaputte Datei und kein Backup, mit dem zweiten Ansatz zumindest noch lokal ein verwertbares Backup. Falls Sie als DBA mehr dazu wissen möchten, suchen Sie in der Online-Hilfe von SQL 2000 oder im Web nach dem Begriff „Log Shipping“.
33
3 Shell-Konfiguration
Abfrage des Exit-Codes unterstützt (beim SQL-Server geben Sie an, welcher Wert von Errorlevel ein erfolgreiches Kommando signalisiert), können Sie das Ergebnis abfragen. Ein Wert von 0 bedeutet erfolgreiche Ausführung. Wann immer Sie also ein Shell-Kommando benötigen, das nicht als externe Programmdatei vorliegt, können Sie /C verwenden.
3.1
Das Shell-Fenster einrichten
Dieser Abschnitt beschreibt die Einrichtungsmöglichkeiten des Konsolenfensters. Stellen Sie sich die Shell so ein, dass Sie optimal damit arbeiten können. Genau wie beim Explorer orientiert sich Microsoft eher an den Endanwendern als am Administrator, so dass auch hier die eine oder andere Änderung sinnvoll ist. Nach einer Änderung werden Sie gefragt, ob diese nur für die aktuell ausgeführte Shell oder die Verknüpfung für das Shell-Fenster geändert werden soll. Um diese Einrichtungsmöglichkeiten zu erhalten, öffnen Sie per rechtem Mausklick auf den Fenstertitel oder (Alt)+(Leertaste) das Systemmenü und wählen dann den Menüpunkt EIGENSCHAFTEN aus. Die Abbildung der Dialogbox erspare ich mir an dieser Stelle, da Sie die folgenden vier Punkte den einzelnen Reitern zuordnen können.
3.1.1
Optionen
Cursorgröße Gibt an, wie groß der Cursor im Shell-Fenster sein soll. Mehr ist dazu nicht zu sagen, suchen Sie sich eine passende Einstellung aus. Anzeigeoptionen Die normale Einstellung ist der Fenstermodus. Wenn Sie lieber den gesamten Bildschirm nutzen möchten, können Sie hier den Vollbildmodus aktivieren. Im laufenden Betrieb können Sie auch mit der Tastenkombination (Alt)+(¢) zwischen Fenstermodus und Vollbildmodus wechseln. Befehlsspeicher Hier legen Sie fest, wie viele Befehle der Befehlsspeicher enthält und wie viele Prozesse verschiedene Befehlsspeicher verwalten können. Zur Bearbeitung des Befehlsspeichers siehe Punkt 3.0.1). Bearbeitungsoptionen Die Checkbox für den Einfügemodus legt fest, ob Zeichen, die in einer Befehlszeile eingegeben werden, die bereits vorhandenen Zeichen überschreiben oder vor diesen eingefügt werden. Der Einfüge-
34
Das Shell-Fenster einrichten
modus ist Standard und auch nützlicher als das Überschreiben von Zeichen. Wenn Sie wissen möchten, welche Bearbeitungsmöglichkeiten Sie für die Befehlszeile haben, sehen Sie auf Seite 31 bei den Tastenkombinationen nach. Der QuickEdit-Modus scheint eine Art geheimer Versionszählung von Microsoft zu sein. Bei NT4 war die Option deaktiviert, bei Windows 2000 aktiviert, bei Windows XP wieder deaktiviert … Aber ernsthaft: Ist diese Option aktiviert, können Sie mit der Maus per Klicken und Ziehen einen rechteckigen Bereich des Konsolenfensters markieren. Drücken Sie nun die Eingabetaste, wird dieser Bereich als Text in die Zwischenablage übertragen. Sehr praktisch für Dokumentationen oder wenn Sie gerade ein Buch über die Windows Shell schreiben! Auch der umgekehrte Weg ist möglich. Mit einem Klick auf die rechte Maustaste wird der derzeitige Inhalt der Zwischenablage in die Befehlszeile übertragen. Sie müssen also nie wieder lange Dateinamen oder Teile der Ausgabe neu eintippen: Markieren, (¢) und die rechte Maustaste reichen. Wenn der rechte Mausklick sofort auf die Markierung folgt, können Sie sogar die Eingabetaste weglassen. Vorsicht bei Text aus der Zwischenablage, der eine oder mehrere Zeilenschaltungen enthält. Da beim Klick auf die rechte Maustaste der Inhalt in die Befehlszeile übernommen wird, gilt eine Zeilenschaltung hier als Eingabetaste, die den Befehl auslöst! Stellen Sie sich als schlimmsten Fall vor, Sie haben ein rd c:\ /s / q und eine Zeilenschaltung in der Zwischenablage und klicken auf die rechte Maustaste … – hoffentlich haben Sie ein Backup! Falls zeichenorientierte Programme genutzt werden sollen, die die Maus unterstützen, darf die Option QUICKEDIT-MODUS nicht aktiviert sein, da sonst die Maus nicht funktioniert, weil die Maussteuerung von der Shell übernommen wird.
3.1.2
Schriftart
Na ja, die Schriftart für das Konsolenfenster. Damit hätten wir diesen Punkt auch schon erledigt, gäbe es da nicht einen Stolperstein. Sie können für die Shell sowohl TrueType-Schriftarten oder eine Rasterschrift einstellen. Diese ist in verschiedenen Größen möglich, die jeweils in Pixeln für einen Buchstaben angegeben werden (z.B.: 7x9 oder 10x18). Diese Rasterschriftart zeigt die Zeichen nicht für alle Zeichensätze (Codepages) korrekt an. Falls Sie also bei einer Ausgabe statt der Umlaute nur merkwürdige Zeichen erhalten, kann es an der Auswahl der Rasterschriftart anstelle eines TrueType-Zeichensatzes liegen (siehe auch Seite 268).
35
3 Shell-Konfiguration
3.1.3
Layout
Hier wird die Größe des Konsolenfensters und die des logischen Fensters eingestellt. Die FENSTERGRÖSSE legt die physikalischen Abmessungen des Shell-Fensters in Zeichen (und damit in Abhängigkeit von der Einstellung für die Schriftart) fest. Die FENSTERPUFFERGRÖSSE legt fest, wie groß das logische Ausgabefenster ist, von dem Sie den unter FENSTERGRÖSSE festgelegten Bereich auf dem Monitor angezeigt bekommen. Diese Einstellung sollte nach Möglichkeit so lang sein, dass Sie die Ausgabe eines Kommandos durch Scrollen nach oben komplett einsehen können, auch wenn sie aus dem sichtbaren Fenster herausgerollt ist. Die FENSTERPOSITION legt die Positionierung des Shell-Fensters in Pixeln von der oberen linken Ecke des Bildschirms fest. Aktivieren Sie die Checkbox AUTOMATISCH POSITIONIEREN, wenn Sie Windows die Entscheidung überlassen wollen, wo ein neues Fenster erscheint. Wollen Sie die Position des Fensters in einem Shell-Script oder WSH-Script festlegen, finden Sie die manuell eingestellten Werte in der Registry (s. Punkt 3.2.8) unter dem Wert WindowPosition (als REG_DWORD, jeweils ein Datenwort für x und y).
3.1.4
Farben
Damit werden die Farben für Vorder- und Hintergrund des Shell-Fensters definiert. Die hier eingestellten Farben finden Sie in der Registry wieder (s. Punkt 3.2.8). Sie können die Farben aber auch jederzeit interaktiv durch das Kommando color (s. Abschnitt 3.3.1) verändern.
3.1.5
Startverzeichnis
Das voreingestellte Verzeichnis (das so genannte Arbeitsverzeichnis) wird wie bei jeder Windows-Verknüpfung in den Eigenschaften dieser Verknüpfung festgelegt. Als Standard findet sich hier das Basisverzeichnis des gerade angemeldeten Benutzers. Klicken Sie zum Ändern mit der rechten Maustaste auf den Punkt im Startmenü, und wählen Sie dann den Menüpunkt EIGENSCHAFTEN. Im danach eingeblendeten Dialogfeld können Sie alle Eigenschaften der Verknüpfung ändern. Diese Einstellung können Sie durch einen beliebigen anderen Pfad ersetzen, wobei Sie wie in der Vorgabe auch Umgebungsvariablen (s. Kapitel 6) benutzen können.
3.1.6
Integration in den Explorer
In vielen Tool-Sammlungen findet sich die Möglichkeit, den Windows Explorer so zu erweitern, dass mit einem Klick der rechten Maustaste auf ein Verzeichnis ein Shell-Fenster geöffnet werden kann. Dieses Fenster benutzt dann das im Explorer selektierte Verzeichnis als aktuelle Basis.
36
Registry Keys
Diese Funktion (und auch andere Aufrufe von Programmen) können Sie jederzeit selbst nachrüsten. Ich möchte daher an dieser Stelle Schritt für Schritt zeigen, wie Sie solche Ergänzungen selbst in die Registry eintragen können. So müssen Sie sich nicht mehr auf andere Programme verlassen. Zusätzlich wissen Sie auch, wo Sie suchen müssen, wenn etwas einmal nicht klappt. Bitte beachten Sie folgenden Hinweis: Änderungen an der Registrierung des Systems können bei Fehlern zum Ausfall des Systems bzw. Datenverlust führen! Sichern Sie vorher auf jeden Fall die Registrierung, und stellen Sie sicher, dass auch eine funktionsfähige Sicherung vorhanden ist. Testen Sie Registry-Änderungen nie an Produktivsystemen, sondern immer erst auf einem Testsystem. Um im Kontextmenü einen Eintrag zum Aufrufen der Shell zu erhalten, gehen Sie wie folgt vor: 1. Suchen Sie in der Registrierung unter HKEY_CLASSES_ROOT den Eintrag Verzeichnis und dort den Schlüssel shell. 2. Darunter legen Sie nun einen neuen Schlüssel an (der Name ist beliebig, sollte aber selbstdokumentierend sein, beispielsweise CmdShell). 3. Darunter legen Sie wieder einen Schlüssel an, der den Namen command tragen muss. 4. Im Standardwert für diesen Schlüssel command (der keinen Namen trägt) geben Sie nun die auszuführende Befehlszeile an, hier cmd.exe "%1". 5. Verlassen Sie nun den Registrierungseditor. Wenn Sie im Windows Explorer auf einen Ordner mit der rechten Maustaste klicken, ist der neue Menüeintrag (hier CmdShell) sichtbar.
3.2
Registry Keys
Für die Konfiguration der Shell findet sich in der Registry eine ganze Reihe von Einträgen. Seien Sie vorsichtig bei der direkten Änderung der Registry! Die meisten dieser Einstellungen können Sie auch ohne einen Eingriff in die Registry per Kommandozeile oder Systemmenü verändern. Dieses Kapitel ist nur für Administratoren gedacht, die aus den Einstellungen hier eigene Policies erstellen wollen. Denken Sie daran, die Registry regelmäßig und vor solchen Eingriffen zu sichern!
37
3 Shell-Konfiguration
3.2.1
HKEY_LOCAL_MACHINE-Einträge
Die nachfolgenden Einträge in der Registry existieren zweimal. Einmal unter dem Root-Key HKEY_LOCAL_MACHINE und einmal unter dem Root-Key HKEY_CURRENT_USER. Die Einträge unter dem letzten Zweig der Registry überschreiben die Einstellungen, die für die Maschine getroffen wurden. Für alle Einträge gilt unterhalb des entsprechenden Root-Keys beide Male der Pfad \Software\Microsoft\ Command Processor.
3.2.2
AutoRun
Unter diesem Schlüssel kann ein Kommando definiert werden, das beim Start der Shell automatisch ausgeführt wird. Dieses Kommando wird allerdings bei jedem Start einer Shell ausgeführt, also auch beim automatischen Aufruf einer Sub-Shell wie beim Gruppieren von Kommandos oder beim Piping! Achten Sie daher darauf, dass dieses Kommando keinen Output produziert, er würde sonst bei einer Filteraktion mit erfasst.
3.2.3
EnableExtensions
Aktiviert oder deaktiviert die Erweiterungen der Shell. Wenn die Erweiterungen aktiviert sind (was den Standard darstellt), wird der Funktionsumfang vieler Kommandos erweitert. Ob ein Kommando sich unterschiedlich verhält, wenn die Erweiterungen aktiv oder inaktiv sind, erfahren Sie aus der Online-Hilfe, wenn Sie das Kommando mit der Option /? aufrufen.
3.2.4
DelayedExpansion
Steuert die Nutzung der verzögerten Variablenexpansion (s. Abschnitt 6.2.6).
3.2.5
CompletionChar
Stellt das Zeichen ein, mit dem die automatische Vervollständigung von Datei- und Verzeichnisnamen aktiviert wird. Der nummerische Wert definiert den Zeichencode der Taste, die für die Vervollständigung benutzt wird. Daher muss es sich bei dem Wert um eine Zahl kleiner als dezimal 32 handeln (0x1F), da bei 32 die druckbaren Zeichen beginnen. Bewährt hat sich die Angabe von 9 (egal ob dezimal oder hex, da bis 9 beide Zahlensysteme übereinstimmen), da dann nicht nur die Tabulatortaste in der Shell einer sinnvollen Verwendung zugeführt wird, sondern automatisch auch die Tastenkombination Umschalt-Tab genutzt wird.
38
Laufzeitkonfiguration
Nachdem diese Funktion seit Windows XP endlich automatisch aktiviert ist, fragt sich (nicht nur) der Autor, was MS damit bezweckt hat, diese Möglichkeit seit NT 3.51 implementiert zu haben, aber dann mehr als zehn Jahre nicht zu aktivieren. Wollte man den Administratoren das Zehnfingersystem beibringen, indem alle Dateinamen mühevoll getippt werden, wenn der Schlüssel in der Registry nicht gefunden wird? Welche nicht aktivierten Arbeitserleichterungen warten noch in den Tiefen des Systems?
3.2.6
PathCompletionChar
Diese Einstellung hat die gleiche Bedeutung wie COMPLETIONCHAR, doch nur für die Vervollständigung von Verzeichnisnamen, falls Sie die beiden getrennt handhaben wollen.
3.2.7
DefaultColor
Legt die Standardfarbe für das Shell-Fenster fest. Ein Wert von 0 liest die Daten aus der Registry aus (s. nächster Absatz).
3.2.8
HKCU\Console Settings
Unter diesem Schlüssel finden Sie die Konfiguration für alle Konsolenprogramme, die Sie bereits über das Systemmenü in ihrem Aussehen und Layout konfiguriert haben. Dort liegt auch unter dem Schlüssel %SYSTEMROOT%_System32_cmd.exe die Konfiguration für die Shell. Nachdem sich alle Informationen auch über das GUI steuern lassen und die Werte relativ selbsterklärend sind, besteht kein Grund, hier mühevoll von Hand zu verändern, was auch bequem über ein Dialogfeld einstellbar ist.
3.3
Laufzeitkonfiguration
3.3.1
color
Das Kommando color stellt für die Konsole die Farben für Vorderund Hintergrund ein (analog zu der Einstellung über die Eigenschaften des Konsolenfensters). Die entsprechenden Farbwerte für den color-Befehl finden Sie in der folgenden Tabelle:
39
3 Shell-Konfiguration Tabelle 3.3: Farbcodes für das Shell-Fenster
0 = Schwarz
8 = Dunkelgrau
1 = Dunkelblau
9 = Blau
2 = Dunkelgrün
A = Grün
3 = Blaugrün
B = Zyan
4 = Dunkelrot
C = Rot
5 = Violett
D = Magenta
6 = Ocker
E = Gelb
7 = Hellgrau
F = Weiß
Ob Sie die Farben per color oder über das Systemmenü des ShellFensters einstellen, ist funktional gleichwertig. Interessanter ist die Verwendung dieses Kommandos in einem Shell Script, um so beispielsweise einen blinkenden Text zu simulieren. Allerdings eignen sich für solche Zwecke zusätzliche Tools wie xecho oder andere meist besser.
3.3.2
Prompt
Mit Hilfe von prompt lässt sich die Eingabemeldung konfigurieren, um beispielsweise auch immer die Zeit oder die Anzahl der noch auf dem Verzeichnisstapel abgelegten Verzeichnisse anzuzeigen. Wird das Kommando ohne Parameter eingegeben, wird als Meldung der aktuelle Pfad, gefolgt von einem Größer-Zeichen, eingestellt. Folgende weitere Platzhalter werden bei der Angabe akzeptiert: Tabelle 3.4: Platzhalter für prompt-Sequenzen
$A
& (Kaufmännisches Und)
$B
| (Verkettungszeichen)
$C
( (Klammer auf)
$D
Aktuelles Datum
$E
Escape-Zeichen (ASCII-Code 27)
$F
) (Klammer zu)
$G
> (Größer-als-Zeichen)
$H
Rückschritt (löscht vorangehendes Zeichen)
$L
< (Kleiner-als-Zeichen)
$N
Aktuelles Laufwerk
$P
Aktuelles Laufwerk und Pfad
$Q
= (Gleichheitszeichen)
$S
(Leerzeichen)
$T
Aktuelle Zeit
$V
Windows-Versionsnummer
$_
Wagenrücklauf und Zeilenvorschub
$$
$ (Dollarzeichen)
$+
Pro mit pushd abgelegtem Verzeichnis ein "+"-Zeichen
$M
Bei Netzwerklaufwerken den Remote-Namen, sonst Leerstring
Den Platzhalter $H benötigen Sie nur dann, wenn Sie Teile der Ausgabe anderer Platzhalter wieder löschen wollen. So gibt zum Beispiel
40
Laufzeitkonfiguration $T die komplette Zeit in der Form hh:mm:ss,cc aus (wobei cc für die
Hundertstelsekunden steht). Wollen Sie im Prompt nur die Stunden und Minuten, können Sie dies mit der Sequenz $T$H$H$H$H$H$H erreichen, die nach der Ausgabe der Uhrzeit wieder sechs Zeichen entfernt. Einige Beispiele: prompt $P$G
Liefert die Standardmeldung, z.B. C:\tmp> prompt $M $p $+ #
Liefert eine Meldung der Form \\server\share R:\ ++ # und zeigt damit an, dass zwei Verzeichnisse auf dem pushdStapel liegen und das Root-Verzeichnis des Laufwerks R: in Wirklichkeit ein Share auf einer Remote-Maschine ist. Möchten Sie beispielsweise etwas mehr *nix-Feeling? Kein Problem: prompt # %computername%: stellt als Meldung ein Hash-Zeichen gefolgt vom Maschinennamen und dem Doppelpunkt ein8.
3.3.3
Title
Gerade für Administratoren sehr praktisch ist das Kommando title, mit dessen Hilfe der Titel des Konsolenfensters definiert wird. Wer ständig mehrere Shell-Fenster geöffnet hat oder eine Shell mit seinem normalen Domain-Account und eine als Administrator benutzt, kann so eine Verwechslung auf einfache Art und Weise vermeiden. Ich habe auf den meisten Maschinen, mit denen ich arbeite, mehr als ein Shell-Fenster offen. Eines mit dem normalen Benutzerkonto und eines mit der Admin-Anmeldung. Dieses ist (per color oder per Eigenschaften im Systemmenü) allerdings mit weißer Schrift auf rotem Hintergrund konfiguriert, und mit Hilfe von title Admin-Shell!! wurde auch der Fenstertitel angepasst. So sehe ich in der Taskleiste auch bei verdeckten Fenstern ganz schnell, welche Shell ich gerade benötige. Die allgemeine Syntax ist sehr einfach: title . Eine Eingabe von title ohne Parameter bewirkt keine Änderung. Ansonsten wird jeder Text als neuer Titel des Shell-Fensters angezeigt9. title Admin-Shell
Sehr praktisch ist die Verwendung dieses Kommandos auch in Shell Scripts (Batch Files), da durch mehrere title-Kommandos in zeitlichem Abstand auch dann eine Information an den Benutzer gelangt, wenn das Shell-Fenster minimiert ist. 8
9
Ganz so vielseitig wie bei bash, tcsh oder anderen Shells des Betriebssystems mit dem Pinguin ist die Meldung allerdings nicht konfigurierbar: Derzeit fehlt die Möglichkeit der dynamischen Ausgabe von Umgebungsvariablen, und einen Platzhalter für die Kommandonummer vermisse ich auch noch. Aus Gründen, die mir noch niemand erklären konnte, werden führende Semikola, Kommata und Gleichheitszeichen vor dem Titeltext ignoriert. Rahmen Sie also Ihre Texte lieber mit Minuszeichen ein …
41
3 Shell-Konfiguration
42
4 4.1
Umleitung & Filter
Dateikanäle
Jede Konsolenanwendung besitzt drei Möglichkeiten für die Ein- und Ausgabe von Daten. Für die Eingabe den Eingabekanal, der normalerweise als Standardeingabe (StdIn) bezeichnet wird. Dieser Kanal besitzt als Kennziffer das Handle 0. Wo etwas eingegeben und verarbeitet wird, wird auch etwas ausgegeben. Für die Ausgabe stehen allerdings aus historischen Gründen zwei verschiedene Kanäle zur Verfügung: die Standardausgabe (StdOut) mit dem Handle 1 und der Standardfehlerkanal (StdErr) mit dem Handle 2. Auch StdErr liegt bei der Shell per Default auf der Konsolenausgabe, so dass Sie normale Ausgabe und Fehlermeldungen zusammen erhalten. Wie ich Ihnen weiter unten noch vorstellen werde, lassen sich mit diesem Konzept aber Fehler und normale Ausgaben recht einfach in zwei verschiedene Dateien aufteilen.
4.2
Dateiumleitung
4.2.1
Ausgabeumleitung
In vielen Fällen wird die Ausgabe eines Befehls nicht benötigt, sondern nur der Errorlevel bzw. der Exit-Code des Prozesses (mehr dazu später). Mit Hilfe der Ausgabeumleitung kann die Ausgabe dann in den „elektronischen Reißwolf“, das sog. Nul-Device geleitet werden. echo Prüfe Erreichbarkeit der Maschine ... ping -n 1 -w 250 kiste.domain.wo > nul if errorlevel 0 programm.exe kiste.domain.wo
Das obige Kommando leitet die Ausgabe des ping-Befehls in das NulDevice um, so erfolgt keine Ausgabe. Innerhalb eines Befehlsstapels wird dann geprüft, ob der ping-Befehl erfolgreich war, und anschließend ein Programm gestartet. Die Umleitung der Ausgabe wird aber auch oft zur Protokollierung von Kommandos genutzt, indem die Ausgabe nicht in die Konsole erfolgt, sondern in eine Logdatei umgeleitet wird. Auch wenn die Ausgabe eine Kommandos weiter verarbeitet werden soll, ist die Umleitung in eine Datei nützlich. So liefert das folgende Kommando eine Liste aller Dateien mit dem Namen index.html inklusive des kompletten Pfades zu dieser Datei. Damit lässt sich dann z.B. automatisiert eine Navigation erzeugen. dir index.html /b /s > indexfiles.txt
43
4 Umleitung & Filter
Soll an eine bereits bestehende Datei angefügt werden, ist das Zeichen ">" zu verdoppeln. Falls die Datei noch nicht existiert, wird diese natürlich vorher angelegt. Wird mit Hilfe des Ausgabeumleitungssymbols > eine Datei erzeugt, wird eine eventuell bereits unter diesem Namen vorhandene Datei immer ohne Rückfrage überschrieben! Wird nur das einfache oder doppelte Größer-Zeichen angegeben, gilt die Umleitung immer für das StdOut-Handle (1). Aber auch das StdErr-Handle lässt sich umleiten. Es ist sogar möglich, die Standardausgabe in eine Datei und die Fehlermeldungen in eine zweite Datei umzuleiten. Geben Sie einfach einmal in einer Shell folgendes Kommando ein: C:\tmp>xcopy NichtDa.txt datei.txt
Sie erhalten dann die unten stehende Ausgabe: Datei NichtDa.txt nicht gefunden. 0 Datei(en) kopiert
Die erste Zeile ist eine Fehlermeldung, die Sie darauf hinweist, dass die Datei NichtDa.txt nicht gefunden wurde (falls Sie Ihre Dateien so benennen, müssen Sie sich einen anderen Beispielnamen suchen). Die zweite Zeile "0 Datei(en) kopiert" ist eine reine Statusmeldung und gibt in jedem Fall (Fehler oder nicht) an, wie viele Dateien abgearbeitet wurden. Jetzt lassen sich diese beiden Informationen in verschiedene Kanäle leiten, indem beim Umleiten der Ausgabe vor dem Größerzeichen das entsprechende Handle angegeben wird: xcopy NichtDa.txt datei.txt 1> log.txt 2> err.txt
In der Datei log.txt finden Sie nun alle normalen Ausgaben (wie die Zeile "0 Datei(en) kopiert"), und in der Datei err.txt finden Sie alle Fehlermeldungen, die zum StdErr-Handle (2) geschrieben wurden. Eigentlich eine sehr feine Sache, die auch eine leichte Aufteilung von Ausgaben erlaubt, würden sich alle Kommandos daran halten. Dies ist nicht der Fall. So gibt ping beispielsweise auch bei einem nicht erreichbaren Host oder einer ungültigen Option die Meldung nach StdOut aus. Möchten Sie die Ausgabedatei nicht überschreiben, sondern erweitern, müssen Sie das Zeichen ">" nur verdoppeln. Das Kommando echo %time% >> ausgabe.txt
sorgt also dafür, dass die Datei ausgabe.txt nach jeder Ausführung des Kommandos eine Zeile mehr enthält. Sollte die Datei vorher noch nicht existiert haben, wird sie beim ersten Aufruf automatisch erzeugt.
44
Dateiumleitung
Leere Dateien erzeugen Wesentlich trickreicher ist das Erzeugen einer leeren Datei mit der Windows Shell. In vielen Fällen (z.B. für Signaldateien, siehe Kapitel 11.6.2) reicht eine leere Datei vollkommen aus. Mittlerweile ist die Platzverschwendung bei NTFS für kleine Dateien nicht mehr so schlimm als noch bei FAT (sehr kleine Dateien finden bei NTFS komplett im Eintrag des Inhaltsverzeichnisses Platz), trotzdem sind noch genügend FAT-Systeme auf Clientseite im Einsatz. Der Trick besteht darin, find (siehe Seite 47) etwas zum Suchen zu geben, das nicht gefunden wird: Mit echo. | find "xxx" > datei.ext erhalten Sie eine Datei mit der Größe 0 Byte.
4.2.2
Eingabeumleitung
Nicht jede Eingabe für ein Shell-Kommandos muss von der Kommandozeile oder vom Benutzer kommen. Mit Hilfe der Eingabeumleitung sind Sie in der Lage, diese Daten direkt aus dieser Eingabedatei an das Shell-Kommando zu übergeben. Das Symbol hierfür ist das Kleinerzeichen (<). Angenommen, Sie benötigen zu Testzwecken bei der Migration auf den Windows 2003-Server eine Liste aller Service-Records, die noch auf einem Namensserver vorhanden sind. Dazu legen Sie (z.B. mit Notepad) eine Textdatei an, die folgenden Inhalt besitzt: server nameserver.domain ls -t SRV domain exit
Speichern Sie diese Datei unter dem Namen input.txt ab. Jetzt können Sie nslookup automatisieren, indem Sie folgende Befehlszeile eingeben: nslookup < input.txt
Wollen Sie die Ausgabe der Service-Records weiterverarbeiten, können Sie beide Umleitungen natürlich miteinander kombinieren: nslookup < input.txt > srv.txt
Eine Möglichkeit, Textdaten vom Benutzer interaktiv eingeben zu lassen, bietet entweder der Set-Befehl (s.u.) oder die Methode, von der Konsole in eine Datei zu kopieren und diese anschließend weiterzuverarbeiten. Dazu kann bereits seit uralten DOS-Zeiten über das Kommando copy con eine Datei angelegt werden. Einziger Nachteil: Der Benutzer muss die Eingabe mit der Tastenkombination (Strg)+(Z) abschließen. An diesem Zeichen (dem Dateiendezeichen in Windows) erkennt die Shell, dass die Eingabe beendet werden soll. Andere Alternativen sind die Nutzung des Windows Script Hosts oder Tools von Drittanbietern.
45
4 Umleitung & Filter
Viele Shell Scripts nutzen solche Eingabeumleitungen, um damit Konsolenprogramme automatisiert mit Input zu versorgen. Sie sollten bei der Erstellung von solchen Dateien jedoch darauf achten, dass wirklich alle Befehle darin enthalten sind. Warum? Ganz einfach: Stellen Sie sich vor, Sie haben für ftp eine Eingabedatei erstellt, um einen Dateitransfer zu automatisieren. Allerdings haben Sie am Ende der Datei das Kommando quit vergessen, um ftp wieder zu beenden. Damit hängt der Prozess sozusagen in der Luft, denn Sie haben die Eingabe umgeleitet. Eingaben von der Tastatur (StdIn) werden damit nicht mehr angenommen, aus der Datei kommt aber nichts mehr. Also bleibt nur noch der Abbruch des Prozesses per taskkill (kill bei Windows 2000) oder Task Manager.
4.2.3
Umleitung und der Windows Script Host
Falls Sie die Ein- oder Ausgabeumleitung oder Piping zusammen mit dem Windows Script Host verwenden wollen, müssen Sie beim Lesen oder Schreiben von StdIn oder StdOut im Script Host immer cscript beim Aufruf mit angeben. Andernfalls werden die entsprechenden Datei-Handles nicht erzeugt, und der Befehl schlägt fehl, wie das folgende Beispiel zeigt: C:\tmp>echo Testtext | test.vbs C:\tmp\test.vbs(1, 1) (null): Das Handle ist ungültig.
Wird dagegen der Script Host beim Aufruf mit angegeben, funktioniert alles. C:\tmp>echo Testtext | cscript test.vbs TESTEXT
4.3
Datenübergabe und Filter
4.3.1
Das Prinzip
Ein- und Ausgabe mehrerer Konsolenprogramme können direkt miteinander verbunden werden. Da die Daten in diesem Fall wie in einer Pipeline reisen, wird dies im Fachjargon auch mit dem Begriff Pipelining oder Piping bezeichnet. Hierbei wird der jeweils nächste Filter durch ein Piping-Symbol („|“) getrennt angegeben10. Dadurch werden Ausgabekanal des ersten und Eingabekanal des zweiten Kommandos miteinander verbunden, so dass Sie sich die Erzeugung von nur temporär benötigten Zwischendateien sparen. Die allgemeine Syntax lautet: kommando1 | kommando2 … | kommando n 10 Falls Sie im Rechenzentrum vor einer US-Tastatur sitzen und das Zeichen nicht finden: Halten Sie die Taste [Alt] fest, und geben Sie auf dem Zehnerblock die Zahl 124 (den Zeichencode) ein, lassen Sie [Alt] los.
46
Datenübergabe und Filter
Zur Anzeige einer nach Prozessnamen sortierten Liste der laufenden Prozesse, die nach jeder vollen Bildschirmseite stoppt, kann folgendes Kommando verwendet werden: tlist | sort /+6 | more
Besonders nützlich ist der Einsatz des Piping-Operators in Verbindung mit besonderen Shell-Kommandos, die als „Filter“ bezeichnet werden. Diese werden nachfolgend detaillierter beschrieben. Als Filter bezeichnet man Programme, die über keinerlei Benutzeroberfläche verfügen und nur über Optionen der Befehlszeile gesteuert werden. Sie lesen über die Standardeingabe, verarbeiten diese Daten und geben das Ergebnis über die Standardausgabe aus. Prinzipiell kann also jedes Programm, das dieser Logik folgt, als Filter eingesetzt werden. Was der Windows Shell auch beim Windows 2003 Server noch fehlt, sind zwei Filter, die in der Unix-Welt beinahe täglich eingesetzt werden: tail und uniq. Der erste zeigt das Ende einer Datei an (eine konfigurierbare Anzahl von Zeilen), und der zweite Filter entfernt aus einem sortierten Eingabestrom alle Duplikate. Diese beiden Filter (und noch viele Unix-Tools mehr) sind unter anderem in den Ports der GNU-Tools zu finden.
4.3.2
find
Das Kommando find sucht zeilenweise nach Texten in Dateien. Die Benutzung kann entweder als Filter mit Hilfe der Umleitungs- und Piping-Operatoren erfolgen oder als normaler Programmaufruf. Hier kann dann auch mehr als eine Datei übergeben werden, die durchsucht werden soll. find [/i] [/c] [/v] [/n] "suchtext" [datei1] [datei...]
Mögliche Optionen für find sind: /i
unterscheidet nicht zwischen Groß- und Kleinschreibung
/v
gibt nur die Zeilen aus, die den Suchtext nicht enthalten
/n
nummeriert die ausgegebenen Zeilen fortlaufend
/c
gibt nur die Anzahl der Zeilen aus, in denen der Suchtext vorkommt
Tabelle 4.1: Optionen für find
Falls bei find eine Übereinstimmung mit dem Suchtext gefunden wird, liefert das Programm einen Errorlevel (s. Abschnitt 6.2.7) von 0 (Erfolg). Wird der Suchtext nicht gefunden, liefert find einen Errorlevel von 1. Durch geschickte Kombination der Optionen von find lassen sich auch ungewöhnliche Aufgaben lösen. Dazu ein Beispiel: Nummerieren Sie die Zeilen einer Textdatei. Das Kommando dazu sieht so aus: find /n /v "@@@@@" < datei.txt
47
4 Umleitung & Filter
Als Suchstring geben Sie eine Zeichenkette an, die in der Datei nicht vorkommt (hier "@@@@@"). Der Schalter /v sorgt dafür, dass alle nicht zutreffenden Zeilen ausgegeben werden (also alle), während /n für die Nummerierung sorgt. Aus der Datei Eins Zwei Drei
wird also [1]Eins [2]Zwei [3]Drei
Sollte Ihnen die Art der Nummerierung nicht gefallen, finden Sie weiter hinten im Buch im Abschnitt 16.3.7 die (zugegebenermaßen etwas komplexere) Kommandozeile, mit der Sie die eckigen Klammern gegen andere Zeichen tauschen können.
4.3.3
sort
Dieses Kommando sortiert die Standardeingabe zeilenweise und gibt diese sortierte Liste in die Standardausgabe aus. Wird eine Datei in der Kommandozeile angegeben, so wird diese anstelle von StdIn verwendet (dieses Verfahren ist schneller als das Umleiten der Standardeingabe). Ebenso ist die Verwendung der Option /O schneller als die Umleitung der Standardausgabe in eine Datei. SORT [/R] [/+n] [/L locale] [/REC recordbytes] Tabelle 4.2: Die wichtigen Optionen für sort
/R
Dreht die Sortierfolge um (reverse sort).
/+
Sortiert beginnend ab der Spalte .
/L
Ändert die Sortierfolge von der Ländereinstellung auf eine andere, derzeit ist allerdings nur "C" möglich (für die schnellste Einstellung).
/REC
Falls die zu sortierenden Zeilen länger als 4096 Zeichen sind, kann hier angegeben werden, wie lang die Zeilensätze sind (max. 65535).
/O Gibt eine Ausgabedatei für das Ergebnis der Sortierung an.
Es gibt auch noch einige weitere Optionen, die beispielsweise die Menge des maximal möglichen Speichers für die Sortierung oder die Platzierung der temporären Daten betreffen. Diese Exoten sind aber für 99,9% der Anwendungen von sort nicht notwendig. Falls Sie daran interessiert sind, geben Sie einfach das Kommando sort /? ein. Eine Möglichkeit, Duplikate aus einer Sortierdatei zu entfernen, ist übrigens auch bei Windows 2003 nicht enthalten. Verwenden Sie dazu eine der vielen Portierungen des Unix-Tools uniq, oder schla-
48
Datenübergabe und Filter
gen Sie im Abschnitt 16.3.8 nach. Dort stelle ich Ihnen ein Shell Script vor, das Duplikate aus einer sortierten Liste entfernt.
4.3.4
more
Dient dem seitenweisen Blättern durch eine Datei. Auch hier ist entweder die Verwendung als Filter oder der Aufruf als Programm mit dem Namen der Datei oder mehreren Dateien als Parameter möglich. more bietet folgende Optionen beim Aufruf: /E
Aktiviert die erweiterten Möglichkeiten.
/C
Löscht den Bildschirm, bevor eine Seite angezeigt wird.
/P
Führt Seitenvorschubzeichen aus.
/S
Fasst mehrere leere Zeilen zu einer Zeile zusammen.
/Tn
Ersetzt Tabulatorzeichen durch n Leerzeichen (Standard 8).
+
Start mit der Ausgabe in Zeile .
Tabelle 4.3: Aufrufoptionen für more
Wenn die Ausgabe am Ende der Bildschirmseite pausiert, haben Sie folgende Möglichkeiten, den weiteren Verlauf zu steuern: [ENTER]
Zeigt die nächste Zeile an.
[Leer]
Zeigt die nächste Seite an.
P
Zeigt die nächsten Zeilen an.
S
Überspringt die nächsten Zeilen.
Q
Beendet die komplette Ausgabe.
F
Zeigt die nächste Datei in der Liste an.
=
Zeigt die aktuelle Zeilennummer an.
?
Zeigt eine Hilfezeile mit diesen Möglichkeiten an.
Tabelle 4.4: Tastenkombinationen für more im interaktiven Modus
Obwohl more beim interaktiven Aufruf oder in einem Batch nach einer Bildschirmseite stoppt, wird diese Funktion bei einer umgeleiteten Ausgabe deaktiviert. Ein more < datei1.txt > datei2.txt wird also auch bei 200 Zeilen in der Datei in einem Rutsch durchlaufen.
4.3.5
clip
Der Filter clip ist bei Windows 2000 noch Bestandteil des Resource Kits, ab XP ist er im Standardumfang der Shell enthalten. Es existieren aber auch einige freie Filter mit der gleichen oder mehr Funktionalität, die von Drittanbietern programmiert wurden.
49
4 Umleitung & Filter
Alles, was an den Filter gesendet wird, landet in der Zwischenablage von Windows, während bei einer Verwendung als Quelle die Zwischenablage ausgelesen wird. Wenn sie beispielsweise die IP-Konfiguration Ihrer Netzwerkkarte in einer Dokumentation weiterverwenden wollen, reicht ein ipconfig /all | clip
und die Daten liegen in der Zwischenablage als Text. Sehr praktisch! Seien Sie vorsichtig beim Einfügen von Inhalten aus der Zwischenablage in die Shell. Jede mit einem Zeilenende (CR/LF) abgeschlossene Zeile wird als Kommando sofort ausgeführt. Mehr Informationen zur Konfiguration dieses Features finden Sie im Abschnitt 3.1.1.
50
5
Dateien im Detail
Dieses Kapitel ist aufgrund vieler Nachfragen bei Kursen ins Manuskript für das Buch gewandert. Sie werden auf den nächsten Seiten weniger neue Kommandos lernen, als vielmehr einige Aspekte von Windows besser kennen lernen, die mit der Behandlung von Dateien und Dateiinhalten zu tun haben. Gerade Administratoren, die von Unix auf Windows umsteigen (müssen), und solche, die Windows bisher nur über GUI-Tools verwaltet haben, erhalten hier Informationen, die das Verhalten des Systems hoffentlich etwas verständlicher erscheinen lassen.
5.1
Warum ist type unbrauchbar?
Ist ein ganzer Abschnitt zum Thema „Wie gebe ich eine Textdatei aus?“ sinnvoll? Ja, da sich hier wieder die historisch bedingten Inkonsistenzen der Windows Shell zeigen. Das Kommando type gibt den Inhalt einer Datei komplett auf die Standardausgabe aus, so erklärt dies zumindest die Windows-Hilfe. Zusätzlich wird dem Benutzer noch davon abgeraten, binäre Dateien mit type auszugeben, da er sonst merkwürdige Zeichen auf dem Bildschirm sehen könnte. Problematisch an type ist aber die Tatsache, dass Sie unter Umständen gar nicht den kompletten Inhalt einer Datei angezeigt bekommen! Für die Demonstrationen in diesem Kapitel habe ich eine Datei erstellt, die sich auch im Download-Archiv zu diesem Buch befindet. Was an dieser Datei so besonders ist, werde ich Ihnen gleich verraten. Doch zuerst möchte ich gerne den Inhalt der Datei ausgeben. Dazu werden die meisten Leser in der Shell sicher das Kommando type verwenden. Hier die Ausgabe: C:\tmp>type t:\Transfer\DateiEOF.txt Es war einmal ein Elch, der arbeitete aushilfsweise in einem Minijob als Rentierersatz beim Weihnachtsmann. C:\tmp>
Sollten Sie dem Inhalt der Datei keinen rechten Sinn abgewinnen, haben Sie aus zwei Gründen Recht. Erstens werde ich mit solchen Texten nie die Belletristik-Listen anführen, und zweitens fehlt ihnen
51
5 Dateien im Detail
das Happy End der Geschichte. Sie sehen hier nämlich nur einen Teil der Datei! Zum Beweis gebe ich die Datei noch einmal aus, diesmal aber mit more11: C:\tmp >more < DateiEOF.txt Es war einmal ein Elch, der arbeitete aushilfsweise in einem Minijob als Rentierersatz beim Weihnachtsmann. Ë Nach einigen Jahren hatte er so viel verdient, dass er sich auf den Bahamas zur Ruhe setzen konnte. C:\tmp>
Während type also die Ausgabe bei einem EOF-Zeichen stoppt (diese Funktionalität ist seit MS-DOS so und konnte daher aus Gründen der Abwärtskompatibilität nicht geändert werden), gibt more eine Datei wirklich bis zum tatsächlichen Ende der Datei aus. Nachdem wir nun unseren Geweihträger sozial abgesichert wissen, stellt sich die Frage, wieso die beiden Kommandos unterschiedliche Ausgaben liefern. Der Grund liegt in dem Zeichen zwischen den beiden Absätzen (dem kleinen Pfeil nach rechts bei der Ausgabe). Dies ist das Zeichen mit dem ASCII-Code 26 und wird EOF (end of file) oder Dateiendezeichen genannt. Über die Tastatur erzeugen Sie das Zeichen mit der Tastenkombination (Strg)+(Z) (da Z der 26. Buchstabe ist). Wie der Name bereits sagt, dient das Zeichen als Markierung für das Ende einer Datei. Achtung, Geschichtsstunde! Falls Sie interessiert sind, ob Windows (bzw. das Dateisystem) überhaupt ein Dateiendezeichen benötigt und wieso das (Strg)+(Z) ist, lesen Sie weiter … Ein Vorfahre von MS-DOS (von dem Windows ja abstammt) war CP/M von Digital Research. Bei CP/M wurden im Inhaltsverzeichnis für eine Datei noch keine tatsächlichen Dateilängen angelegt. Stattdessen wurde dort die Länge der Dateien in Datensektoren eingetragen. Da zu Zeiten von CP/M Speicher (auch auf Disketten) knapp und teuer war, betrug die Sektorlänge 128 Byte. War der letzte Sektor einer Datei kleiner als 128 Byte, war das auch kein Problem, da die Sektoren mit einem Füllzeichen aufgefüllt wurden. Dies war das Zeichen mit dem ASCIICode 26 (eben (Strg)+(Z)). Ein Programm las also so lange komplette Sektoren, bis alle gelesen wurden oder bis im letzten Sektor ein Zeichen mit dem Code 26 gefunden wurde.
11 Die Windows Shell erlaubt auch die Syntax more ohne die Umleitung der Eingabe.
52
NTFS Alternate Data Streams
Ab MS-DOS enthielt das Verzeichnis dann die tatsächliche Länge der Datei in Byte, und damit wurde ein spezielles Zeichen als Kennung für das Ende einer Datendatei unnötig. Wenn Sie heute über die Konsole eine Datei eingeben (mit der Methode copy con ), wird die Eingabe zwar mit Strg-Z abgeschlossen, aber dieses Zeichen wird nur benötigt, damit die Shell erkennt, dass Sie keine weiteren Zeichen eingeben wollen. Das Zeichen 26 selbst wird nicht mehr in die Datei geschrieben. Machen Sie den Test: Kopieren Sie Daten aus der Konsole in eine Datei, und sehen Sie sich das Ergebnis mit einem Hex-Editor (oder debug) an. Sie werden dort kein EOF-Zeichen finden. Heutige Dateisysteme wie NTFS verwalten sowieso mehr als einen Datenstrom und sind intern ganz anders organisiert. Aber alte Gewohnheiten wie Strg-Z leben lange …
5.2
NTFS Alternate Data Streams
Eigentlich ist dies kein direktes Thema für das Shell Scripting. Allerdings sind die Möglichkeiten, die sich durch diese Funktion von NTFS ergeben, für Administratoren so wichtig, dass ich es an dieser Stelle aufgenommen habe. Nicht nur für den Umgang mit der Shell, sondern auch aus Gründen der Sicherheit Ihrer Server sollten Sie sich das folgende Kapitel durchlesen, wenn Ihnen der Begriff alternata data stream noch nichts sagt.
5.2.1
Einführung
In einem meiner Windows 2000-Kurse wurde ich von einem Kursteilnehmer gefragt, ob ich mit der unten abgebildeten Dialogbox etwas anfangen könne: Abbildung 5.1: Die mysteriöse Abfrage
Diese Abfrage taucht immer dann auf, wenn Sie eine Datei von einem NTFS-Datenträger auf einen Datenträger mit einem anderen Dateisystem (z.B. FAT32) verschieben oder kopieren. Diese „nicht beibehaltenen“ Informationen sind so genannte alternate data streams (ab hier als
53
5 Dateien im Detail
ADS bezeichnet), ein Standardfeature von NTFS, das trotz seines mittlerweile fortgeschrittenen Alters immer noch recht unbekannt ist12.
5.2.2
NTFS-Dateien
Eine NTFS-Datei wird nicht einfach wie bei FAT als ein Strom von Bytes auf der Platte und ein paar Metadaten im Verzeichniseintrag aufgefasst. Unter NTFS besteht jeder Eintrag im Verzeichnis (der so genannten master file table, abgekürzt MFT) aus einem Datensatz mit einer festen Länge von 1 KByte. Daher wird für kleine Dateien (wie der berühmten test.txt mit drei Zeilen) auch kein Platz verschwendet. Diese Datei passt mit allen Metadaten komplett in den Datensatz in der MFT. Die Identifikation einer Datei erfolgt durch einen 64 Bit großen Wert, der aus 48 Bit für die Dateinummer (die Position in der MFT, gezählt ab 0) und 16 Bit für eine Sequenznummer besteht. Die Sequenznummer wird immer dann um eins erhöht, wenn die gleiche Dateinummer auf einem Datenträger wieder verwendet wird. Die Datei selbst ähnelt (ebenso wie das gesamte Design von NTFS) eher den Objekten einer relationalen Datenbank. Eine Datei wird als eine Kombination von Attributen und ihren Werten aufgefasst, stellt logisch also eine Tabelle dar. Die Daten der Datei (der Inhalt) sind dabei nur ein Teil einer Reihe von Attributen. Dieses (intern mit dem Attributnamen $DATA) Attribut wird als unbenanntes Datenattribut oder auch als Hauptdatenstrom bezeichnet. Diese Attribute können optional noch eine zusätzliche Namensbezeichnung tragen. Da der Hauptdatenstrom keinen optionalen Namen trägt, wird er eben auch als unbenanntes Datenattribut bezeichnet. Weitere Attribute speichern Metadaten wie beispielsweise die Angaben im Verzeichnis (Zeit, System, Schreibgeschützt, Letzter Zugriff) unter dem Attribut $STANDARD_ INFORMATION, den Dateinamen im Unicode-Format ($FILE_NAME) und so weiter13. Wenn Sie mehr darüber erfahren möchten, finden Sie im Buch „Inside Windows 2000“ (Daten im Anhang) eine detaillierte Beschreibung des NTFS-Dateisystems. Der für uns interessante Punkt ist nun die Tatsache, dass durchaus mehr als ein Attribut $DATA vorhanden sein kann. Diese weiteren werden dann zur Unterscheidung mit einem Namen versehen und als alternate data streams (zusätzliche Datenströme oder Datenattribute) bezeichnet. In der Auflistung im Verzeichnis (egal ob im Explorer oder per dir) wird immer nur das unbenannte Datenattribut verwendet. 12 Was natürlich auch daran liegen kann, dass Microsoft keinerlei Tools (noch nicht einmal auf Shell-Ebene) für die Bearbeitung bereitstellt und die ADS in Dokumentationen für Administratoren mit dem Hinweis „this is a developer feature“ abtut. 13 Interessant ist auch die Tatsache, dass diese Attribute über ein „in_use“-Flag verwaltet werden. Wird also ein Attribut gelöscht, wird das entsprechende „in_use“-Flag gelöscht, die Daten sind nach wie vor aber noch vorhanden.
54
NTFS Alternate Data Streams
5.2.3
Erzeugen und Abfragen von ADS
Die einfachste Möglichkeit, einen Datenstrom mit einem eigenen Namen zu erzeugen, besteht in der Verwendung der normalen Ausgabeumleitung. Hier wird eine Versionskennzeichnung als eigener Datenstrom an die Datei report.txt angefügt. echo Ver 1.2.0 - 2003-08-23 > report.txt:version
Eine Erweiterung dieses Datenstroms geschieht wie bei einer regulären Datei über ein Anfügen. echo Ver 1.2.1 - 2003-08-26 >> report.txt:version
Auch Windows selbst nutzt diese Möglichkeit der zusätzlichen Datenströme. Haben Sie sich nicht schon einmal gefragt, wo die ganzen Daten der Dateiinformation bleiben, die Sie über die rechte Maustaste und den Eigenschaftsdialog eingeben können (Autor, Titel, Stichwörter etc.)? Auch dafür werden die ADS genutzt, allerdings mit einem vordefinierten Namen. Dieser Name entspricht dem der COMDokumente (z.B. Office-Dateien) für Zusammenfassungen und lautet ^ESummaryInformation. Das „^E“ zu Beginn steht für das Zeichen mit dem ASCII-Code 5 ((Strg)+(E)) und wird auf der Konsole beim Eintippen so angezeigt14. Machen Sie den Test: Geben Sie für eine NTFS-Datei einige Informationen für eine Datei ein, und sehen Sie sich dann diesen Datenstrom an. Leider ist dieser binär kodiert und nicht direkt lesbar, Sie werden aber die Wörter für Titel und Stichwörter erkennen: more < report.txt:^ESummaryInformation
Diese Funktionalität ist direkt in die Low-Level-Dateiroutinen von Windows integriert. Alles, was einen Dateinamen akzeptiert, akzeptiert üblicherweise auch einen Stromnamen (mit Ausnahme von type!). Daher ist auch Notepad ein tauglicher Editor für zusätzliche Datenströme: notepad buget.txt:revision15
Da type auch hier nicht zu gebrauchen ist, empfehle ich Ihnen an dieser Stelle, sich an die Benutzung von more zu gewöhnen. Falls Sie jetzt einwenden möchten, dass ich alternativ ja auch per doskey einen Alias für type erzeugen kann, der einfach more aufruft: Erstens kann more in der Syntaxvariante more [datei] (ohne das Kleinerzeichen) auch nichts mit ADS anfangen15, und zweitens ändert dies auch das Verhalten des Befehls (type bleibt schließlich nicht nach jeder Seite stehen und wartet). 14 In der Literatur (z.B. der WSH-Dokumentation) wird bei Tastenkombinationen ^ üblicherweise als Präfix für Control ((Strg)), % für (Alt) und + für (ª) verwendet. 15 Falls jemand bei Microsoft mitliest: Warum eigentlich? Es wäre wirklich schön, wenn die Shell wenigstens innerhalb eines Kommandos konsistent wäre.
55
5 Dateien im Detail
Dies ist ein Feature des Dateisystems (übrigens bereits seit NT 3.x!), daher ist der einzige Nachteil der, dass beim Kopieren oder Verschieben auf einen Datenträger mit einem anderen Dateisystem (FAT32, ISO-CD etc.) die Daten in der ADS unwiederbringlich verloren gehen. Bleibt die Datei aber während ihrer gesamten Lebensdauer auf einem NTFS-Volume, ist dies eine ideale Möglichkeit, unabhängig vom Dateninhalt zusätzliche Metadaten zu hinterlegen. Dies funktioniert übrigens auch, wenn der unbenannte Datenstrom leer ist, wie die folgende Sequenz von Befehlen zeigt: C:\tmp>echo. | find "nix" > x.x C:\tmp>dir x.x Datenträger in Laufwerk C: ist Lokaler Datenträger Datenträgernummer: 8C3F-34DE Verzeichnis von C:\tmp 20.06.2004
22:31 1 Datei(en) 0 Verzeichnis(se),
0 x.x 0 Bytes 2.930.911.744 Bytes
frei C:\tmp>echo Version 1 der Datei > x.x:Revision C:\tmp>dir x.x Datenträger in Laufwerk C: ist Lokaler Datenträger Datenträgernummer: 8C3F-34DE Verzeichnis von C:\tmp 20.06.2004
22:32 1 Datei(en) 0 Verzeichnis(se),
0 x.x 0 Bytes 2.930.911.744 Bytes
frei C:\tmp>more < x.x:revision Version 1 der Datei C:\tmp>
Diese Daten sind nicht auf reine Textinformationen begrenzt und stellen ein potenzielles Sicherheitsproblem dar, solange die Datei auf einem NTFS-Datenträger verbleibt. Anhand eines einfachen Beispiels möchte ich Ihnen das demonstrieren: Legen Sie eine Textdatei report.txt an (mit Notepad oder was Sie gerade benutzen). und geben Sie zwei oder drei Zeilen Text ein. Anschließend sehen Sie nach, wie groß die Datei ist: Listing 5.1: Erzeugen der Datei report.txt
56
C:\tmp>echo Windows trennt echo seine Zeilen > report.txt C:\tmp>echo durch die Kombination CR und LF. >> report.txt C:\tmp>dir report.txt Datenträger in Laufwerk C: ist Datengrab
NTFS Alternate Data Streams Datenträgernummer: 8BBF-50DE Verzeichnis von C:\tmp 18.06.2004
22:41 1 Datei(en) 0 Verzeichnis(se),
70 report.txt 70 Bytes 2.929.150.464 Bytes
frei C:\tmp>
Mit den obigen Daten ist die Datei 70 Byte groß, wie die Ausgabe von dir auch anzeigt. Jetzt ergänzen wir die Datei um einen ADS, diesmal aber binärer Natur, und sehen uns dann noch einmal die Größe an16: C:\tmp>type c:\winnt\system32\notepad.exe > report.txt:machwas.exe C:\tmp>dir report.txt Datenträger in Laufwerk C: ist Datengrab Datenträgernummer: 8BBF-50DE Verzeichnis von C:\tmp 18.06.2004
22:41 1 Datei(en) 0 Verzeichnis(se),
70 report.txt 70 Bytes 2.929.150.464 Bytes
frei C:\tmp>
Wir haben eine komplette Kopie von Notepad als ADS in die Datei report.txt integriert, und die Anzeige teilt uns mit, dass die Datei immer noch die Größe von 70 Byte besitzt! Was dir (und die anderen Tools) ausgibt, ist immer die Größe des Hauptdatenstroms, und diese hat sich ja auch nicht verändert17. Nun aber zum interessanten Teil: Wie kann diese ausführbare Datei, die in report.txt klebt, aktiviert werden? Der normale Weg über report.txt:machwas.exe
funktioniert nicht, und es wird eine Fehlermeldung ausgegeben. Grund ist der Parser der Shell, der einen Doppelpunkt beim Starten eines Programms zwar akzeptiert, aber nur als zweites Zeichen hinter dem Laufwerksbuchstaben. Daher müssen wir etwas kreativer werden und eines der neuen Shell-Kommandos nutzen: start .\report.txt:machwas.exe
16 type funktioniert hier, da kein EOF an unpassender Stelle steht, falls Sie sich das gerade gefragt haben. Ansonsten empfehle ich die Benutzung von Tools wie cat. 17 Die gute Nachricht für alle besorgten Administratoren: das Quota Management von Windows rechnet ab Windows 2000 korrekt die gesamte Größe der Datei inklusive aller Datenströme. Nur per dir die Größe zu addieren reicht aber nicht mehr.
57
5 Dateien im Detail
Dieses Kommando startet den Datenstrom machwas.exe in der Datei report.txt, wobei das „.\“ angegeben werden muss, da start den Dateinamen sonst als URL auffasst. Falls Sie eine solche Datei gestartet haben, sehen Sie dies ab Windows XP auch im Task-Manager bzw. bei der Ausgabe von tasklist: Abbildung 5.2: Eine Datei mit ADS im Task-Manager
Die Abbildung oben zeigt in der markierten Zeile den Namen der Datei und den ADS, so dass Sie zumindest wissen, wo Sie suchen müssen. Mit WMI ist eine Remote-Abfrage von Prozessen, die einen Doppelpunkt im Namen tragen, dann auch kein Problem mehr. Um einen Kollegen zu zitieren, der eine solche Query regelmäßig laufen ließ: „Du hast keine Ahnung, was Du da alles findest!“ … Seien Sie also vorsichtig, wenn Sie Dateien auf NTFS-Datenträgern haben. Jeder mit Schreibrechten kann sich einen ADS anlegen und nutzen. Ein Teilnehmer hat mir von einem Benutzer erzählt, der in seinem Home-Verzeichnis eine Textdatei mit einigen Telefonnummern liegen hat. Dahinter in einem ADS steckt eine 300 MByte große ZIP-Datei mit MP3!
5.2.4
Auflisten und Löschen von ADS
In Windows existieren keine Tools, um für eine bestimmte Datei oder ein Verzeichnis aufzulisten, ob und welche Datenströme in einer NTFSDatei vorhanden sind (schließlich ist das ja ein „Developer-Feature“). Es gibt aber im Web mittlerweile eine ganze Reihe von Freeware-Tools, die genau diese Funktionalität bieten und ADS auch wieder aus einer Datei entfernen können. Als Beispiel (für die Kommandozeile) möchte
58
NTFS Alternate Data Streams
ich an dieser Stelle wieder einmal ein Tools von www.sysinternals.com empfehlen. Dieses Tools heißt auch streams und ist (etwas versteckt) bei den Utilities für Windows 2000 und aufwärts unter der Rubrik „Miscellaneous“ (Verschiedenes) und dort noch einmal bei „Miscellaneous“. im Archiv streams.zip zu finden. Eine Alternative ist LADS von www.heysoft.de oder eine Suche in Google. Eine andere Möglichkeit zum Löschen aller ADS: Kopieren Sie die Datei auf eine FAT32-Partition. Falls keine Dialogbox wie oben erscheint, waren keine ADS enthalten. Erscheint die Dialogbox, bestätigen Sie den Verlust der ADS und haben anschließend auf dem FAT32-Datenträger eine Datei, die wieder nur aus dem Hauptdatenstrom besteht und gesäubert wieder zurück auf die NTFS-Partition kopiert werden kann. Haben Sie nur noch NTFS, auch kein Problem. Dazu muss nur der Hauptdatenstrom ausgegeben und in eine Datei umgeleitet werden. Diese Datei wird dann über die alte Datei kopiert. Für Textdateien ist dies unkritisch, achten Sie nur darauf, dass auch alle Zeichen ausgegeben werden (nicht type verwenden, siehe Abschnitt 5.1). ren datei_mit_ads.txt temp.txt more < temp.txt > datei_mit_ads.txt del temp.txt
Problematischer wird es mit Binärdateien wie Programmen. Hier muss ein Tool verwendet werden, das eine Datei 1:1 auf der Konsole ausgibt, damit die umgeleitete Datei auch wieder eine exakte Kopie des Inhalts darstellt. Bei früheren Windows-Version existierte im Resource Kit zu diesem Zweck eine Portierung des Unix-Kommandos cat. Falls Sie kein altes Resource Kit haben, kein Problem: Bei den GNU-Ports der wichtigsten Unix Text-Tools ist ebenfalls ein cat enthalten: ren datei_mit_ads.txt temp.txt cat --binary temp.txt > datei_mit_ads.txt del temp.txt
5.2.5
Verknüpfungen und ADS
Bei Windows 2000 war es noch ohne Probleme möglich, eine Verknüpfung anzulegen und nachträglich das Ziel vom Dateinamen auf Dateinamen und ADS-Namen abzuändern. Damit waren auch ADS in Dateien per Verknüpfung ausführbar. Bei Windows 2003 ist dieses Verhalten geändert worden18. Sie können zwar noch eine Verknüpfung anlegen und diese nachträglich so abändern, dass auf den ADS verwiesen wird. Beim Starten der Verknüpfung per Doppelklick aber wird diese wieder auf den Namen der Datei geändert, und Sie erhalten die Dialogbox, die nach dem für diese Datei zuständigen Programm fragt. 18 Ob dies absichtlich geschah oder nur als Nebeneffekt einer anderen Änderung, konnte ich nicht herausfinden.
59
5 Dateien im Detail
5.2.6
Problemfeld Makro-Viren
Die wirkliche Sicherheitsproblematik liegt aber auch in der Möglichkeit, Code für den Windows Script Host in einem ADS zu verstecken. Es existieren bereits Viren, die eine solche Technik nutzen, daher sollten Sie sich als Administrator mit dieser Möglichkeit beschäftigen. Zuerst stecke ich einen einfachen Scriptcode in einen ADS: Eine Zeile
echo MsgBox "Viele Gruesse vom WSH!!" > list.txt:script.vbs
Dieses Script kann wie jedes andere Programm mit dem im vorletzten Abschnitt beschriebenen Kommando gestartet werden. Also wird ein böswilliger Benutzer versuchen, diesen Code zu verbergen: Eine Zeile
echo MsgBox "Viele Gruesse vom WSH!!" > list.txt:Version
Da der Datenstrom nun keine Erweiterung mehr besitzt, muss beim WSH die zu verwendende Engine mit //E angegeben werden: cscript //E:vbs.\list.txt:Version
Um unauffällig zu bleiben, wird dann oft ein Name gewählt, der auch dem misstrauischen Admin nicht auffällt: Eine Zeile
Wie Sie sehen, sind ADS bei weitem nicht nur ein Feature für Entwickler, sondern gerade Administratoren sollten wissen, welche Möglichkeiten jeder normale Benutzer hat, Daten auf relativ unauffällige Weise zu verstecken. Sinnvoll genutzt bieten sich für Admins und Supporter einige Möglichkeiten, Daten auf NTFS-Datenträgern mit Zusatzinformationen auszustatten. Auf jeden Fall sollte das Thema aus den dunklen Ecken undokumentierter Funktion herausgeholt und auch in Schulungen vermittelt werden. Weitere Informationen, vor allem über die technische Realisierung von ADS, den Aufbau von NTFS-Dateieinträgen und die Möglichkeiten, ADS in eigenen Anwendungen zu nutzen, finden Sie in der MSDN-Library (http://msdn.microsoft.com) oder im Web (geben Sie als Suchbegriff "alternate data streams" (mit den Anführungszeichen) ein. Google beispielsweise liefert dann Hunderte von Treffern vom White Paper über C++ Sourcecode bis zu Tools zum Auslesen von ADS.
60
Echte Links
5.3
Echte Links
NTFS ist in der Lage, so genannte hard links zu erstellen. Dies ist ebenfalls ein kaum genutztes Feature von NTFS, das auch andere Dateisysteme (z.B. unter Linux) bieten. Hier kann für eine bestehende Datei ein zusätzlicher Verzeichniseintrag geschaffen werden (der in einem zusätzlichen Attribut $FILE_NAME des MFT-Eintrags gespeichert wird). Der Pfad kann dabei durchaus ein anderer sein. Ab diesem Zeitpunkt ist die Datei ebenfalls über diesen Namen ansprechbar, wobei das Dateisystem keinen Unterschied zwischen den beiden Namen macht. Im Gegensatz zu den bekannten Verknüpfungen (den shortcuts) wird dafür auch keine zweite Datei angelegt. Die Erzeugung eines solchen Links übernimmt (neben anderen Aufgaben) das Kommando fsutil. Im folgenden Beispiel werde ich zuerst eine Datei anlegen und dann einen hard link darauf setzen: C:\tmp>echo Das ist eine Datei > c:\tmp\test.txt C:\tmp>pushd d:\docs D:\docs>fsutil hardlink create demo.txt c:\tmp\test.txt Hardlink created for D:\docs\demo.txt <<===>> c:\tmp\ cmds.txt D:\docs>dir Volume in drive D is Bunker Volume Serial Number is 2834-CA45 Verzeichnis of D:\docs 05.07.2003 05.07.2003 11.06.2003
Wie Sie sehen, existiert nun eine Datei demo.txt, die ganz normal behandelt werden kann. Aus diesem Grund ist für das Löschen von echten Links auch kein besonderes Kommando notwendig. Sie löschen einfach mit einer der vielen Möglichkeiten von Windows die Datei. Falls dieser Eintrag für das Attribut $FILE_NAME nicht der letzte der Datei war, bleibt die Datei noch erhalten, nur der Link ist weg. Erst wenn das letzte $FILE_NAME-Attribut aus der Datei entfernt wird, wird die Datei tatsächlich gelöscht. Dies erlaubt das völlig transparente Einbinden von hard links in die Arbeit mit Windows.
61
5 Dateien im Detail
62
6
Umgebungsvariablen
Das Konzept der Umgebungsvariablen ist auch aus anderen Betriebssystemen bekannt. Dynamische Informationen oder Daten, die einfach abrufbar sein sollen, werden in der sog. Umgebung einer Session oder eines Prozesses abgelegt. Mit Hilfe von Shell-Kommandos oder GUI-Tools kann der Benutzer oder ein Prozess diese Werte auslesen und verändern. Eine ganze Reihe von Variablen wird vom System beim Start automatisch belegt, vom Installationsverzeichnis des laufenden Windows-Systems bis hin zur Prozessoridentifikation. Die Notation für diese Variablen folgt dem Schema name=wert und kennt als Datentyp nur Zeichenketten.
6.1
Grundlagen
6.1.1
Umgebungstypen
Es gibt mehrere Umgebungsvarianten. So besitzt jeder Prozess einen Speicherbereich, in dem die Umgebung für diesen Prozess definiert ist. Jeder Benutzer bekommt ebenfalls einen Umgebungsbereich (aus dem dann die Umgebung für die vom Benutzer gestarteten Prozesse erzeugt wird). Schließlich existiert noch ein Umgebungsbereich, der die Variablen enthält, die systemweit definiert sind und beim Login für jeden Benutzer kopiert werden. Ein normaler Benutzer kann daher die vom System geerbten Umgebungsvariablen zwar ändern, allerdings für seine Session und die darin laufenden Prozesse. Nur Administratoren sind in der Lage, die Einstellungen für Systemvariablen zu verändern. Nachdem jede Shell, die gestartet wird, einen eigenen Prozess darstellt, wirken sich Änderungen, die Sie in einer Shell treffen, auch nicht auf die Umgebung in einer anderen Shell aus.
6.1.2
Auflisten von Variablen
Mit Hilfe der Kommandos set und setx werden die Variablen auf Kommandozeilenebene verwaltet. In den meisten Fällen werden Sie mit set bestens zurechtkommen. setx (seit Windows 2003 im Standardumfang der Shell, vorher im Resource Kit) benötigen Sie nur dann, wenn Sie Änderungen an den Systemvariablen durchführen wollen.
63
6 Umgebungsvariablen
Die Eingabe von set ohne weitere Parameter listet alle definierten Variablen und ihre derzeitigen Inhalte auf. Da diese Ausgabe durchaus mehrere Bildschirmseiten lang sein kann, können Sie als Parameter eine Zeichenkette angeben. Es werden dann alle Variablen ausgegeben, deren Name mit dieser Zeichenkette beginnt. Daher erhalten Sie bei der Angabe set path auch immer zwei Zeilen; es existiert neben der Variablen path auch eine vordefinierte Variable pathext, deren Name ebenfalls mit der Zeichenkette „path“ beginnt. Path=C:\WINNT\system32;C:\WINNT;C:\Programme\Support Tools\ PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH
Wird keine Variable mit dem angegebenen Namensbeginn gefunden, erhalten Sie eine Fehlermeldung. Mit Hilfe des Kommandos if und der Option defined können Sie testen, ob eine Variable mit dem angegebenen Namen existiert oder nicht.
6.1.3
Vordefinierte Variablen
Die bei den Befehlserweiterungen aktivierten, automatisch erzeugten Variablen können vom Benutzer durch eine eigene Definition überschrieben werden und enthalten dann natürlich die vom Benutzer zugewiesenen Werte. Dies gilt auch für ERRORLEVEL. Die nachfolgende Tabelle zeigt eine Beispielausgabe der vordefinierten Variablen auf einer Maschine: Tabelle 6.1: Liste der vordefinierten Umgebungsvariablen
64
Variablenname
Beispielinhalt
ALLUSERSPROFILE
Profilpfad zum Default-Benutzerprofil
APPDATA
Pfad zu den Anwendungsdaten des aktuellen Benutzers
Sie können jederzeit noch eigene Umgebungsvariablen erzeugen. Innerhalb des Namens darf kein Gleichheitszeichen vorkommen, da dieses den Namen vom zugewiesenen Wert trennt. Variablennamen unterscheiden nicht zwischen Groß- und Kleinschreibung. Um einer Variablen einen neuen Wert zuzuweisen oder eine Variable neu anzulegen, geben Sie nach dem Namen der Variablen ein Gleichheitszeichen gefolgt vom Inhalt der Variablen an: SET database=Northwind
65
6 Umgebungsvariablen
Dies setzt den Inhalt der Variablen database auf den Wert „Northwind“. Beachten Sie, dass sämtliche führenden Leerzeichen (die nach dem „=“) mit zum Inhalt der Variablen werden. Ebenso werden nachfolgende Blanks nicht abgeschnitten, wenn diese angegeben werden. Alle Änderungen, die Sie mit set durchführen, gelten nur für die Umgebung (environment) des laufenden Prozesses (hier der Shell). In einem anderen Shell-Fenster oder den Systemeigenschaften werden Sie diese Änderungen also nicht wiederfinden.
6.2.2
Arbeiten mit setx
Seit Windows 2003 existiert eine Möglichkeit, auch von der Shell aus globale Änderungen am Environment durchzuführen. Dafür dient das Kommando setx. Vor Windows 2003 bleibt Ihnen nur der Weg über die Systemeigenschaften (für die es mit der Kombination Windows-Taste + Pause übrigens einen praktischen Shortcut gibt) oder der undokumentierte Weg über die Registry und die dort verankerten globalen Umgebungsvariablen. setx ist ein relativ komplexes Kommando und erlaubt das Erzeugen oder Verändern von Umgebungsvariablen der System- oder Benutzerumgebung. Während für die Benutzerumgebung mit set schon immer ein Kommando vorhanden war, besteht erst seit setx offiziell die Möglichkeit der Bearbeitung von Systemvariablen.
Drei Optionen von setx möchte ich gerne vorab erläutern, da diese sonst die Kommandosyntax unnötig verlängern würden. Für die Kommunikation mit anderen Maschinen oder die Nutzung eines alternativen Benutzerkontexts wird eine Möglichkeit der Authentifizierung benötigt. Hierzu dienen drei Optionen: Tabelle 6.2: Authentifizierungsoptionen für setx
/S <system>
Name des Remote-Systems
/U [<domain>\]<user> Benutzerkontext, unter dem das Kommando
läuft /P <password>
Passwort für Benutzer. Wird erfragt, falls fehlend
Die restliche Syntax beschränkt sich auf drei Aufrufmöglichkeiten: 1. SETX var value [/M] 2. SETX var /K regpath [/M] 3. SETX /F file {var {/A x,y | /R x,y string}[/M] | /X} [/ D delimiters]
Hier steht var immer für den Namen der Umgebungsvariablen und value für den zugewiesenen Wert. Die Angabe der Option /M bewirkt
66
Arbeiten mit Variablen
eine Änderung der Systemumgebung (das „M“ steht für machine wide), so dass die Änderungen in der Registry unter HKEY_LOCAL_ MACHINE abgelegt werden und nicht unter HKEY_CURRENT_USER. Normales Setzen von Variablenwerten Die erste Möglichkeit entspricht der Benutzung von set mit dem Unterschied der Option /M zum Ändern der Systemumgebung. Deshalb schenke ich mir hier weitere Erläuterungen. Variablen aus der Registry setzen Die zweite Aufrufmöglichkeit stellt eine deutliche Arbeitsersparnis gegenüber den Vorgängerversionen von Windows 2003 dar. Die Option /K (für key) liest einen Registry-Schlüssel aus und verwendet dessen Wert als neuen Inhalt der Variablen. setx tzone /K HKEY_LOCAL_MACHINE\System\ CurrentControlSet\Control\TimeZoneInformation\ StandardName
Eine Zeile
Vor Windows 2003 war für eine solche Aktion etwas mehr Aufwand Windows 2000 nötig. Zuerst musste mit Hilfe des Kommandos reg aus den Support Tools der entsprechende Schlüssel abgefragt werden: reg query HKLM\System\CurrentControlSet\Control\ TimeZoneInformation /v StandardName
Eine Zeile
Diese Ausgabe musste von allen Zeilen außer der Datenzeile befreit werden. Dazu wird per find die Zeile mit dem Datentyp gesucht (die alle mit "REG_" beginnen): reg query HKLM\System\CurrentControlSet\Control\ TimeZoneInformation /v StandardName | find "REG_" > %temp%\tmp.dat
Eine Zeile
Diese Datei wird nun per for gelesen und in eine Zuweisung der Variablen verwandelt: for /F "tokens=1,2,*" %f in (%temp%\tmp.dat) do @set tzone=%h
Eine Zeile
Variablen aus einer Datei setzen Die Syntaxvariante Nummer 3 schließlich erlaubt das Setzen von Umgebungsvariablen aus einer Datei. Aus diesem Grund existiert auch eine ganze Reihe von Optionen, mit denen die Dateiposition, Begrenzungszeichen oder Suchstrings definiert werden können.
67
6 Umgebungsvariablen Tabelle 6.3: Die Optionen für die Dateioperationen von setx
Option
Bedeutung
/F
Gibt den Namen der zu lesenden Datei an
/A x, y
Absolute Positionsangabe in der Datei (Zeile x, Wort y)
/R x, y <string>
Relative Positionsangabe zu <string> (Zeile x, Wort y)
/X
Gibt die Datei mit Positionsangaben aus (nur mit /F, /D)
/D <delims>
Definiert die Trennzeichen zwischen Wörtern
Achten Sie darauf, dass alle Zeilen- und Wortangaben nullbasiert sind! Hierzu ein Beispiel: Um den Wert der aktuell zugewiesenen IPAdresse in eine Umgebungsvariable zu bekommen, gibt es zwei Möglichkeiten. Die einfachere ist die für Windows 2003 (die für Windows 2000 finden Sie im Praxisteil19). Dazu wird mit ipconfig die IP-Konfiguration ausgegeben und in eine Datei umgeleitet. Anschließend muss nur noch bestimmt werden, welche Position in der Datei die IP-Adresse hat, und die setxAnweisung erzeugt werden. ipconfig > %temp%\tmp.dat
Jetzt lassen wir mit setx /X /F %temp%\tmp.dat die Dateipositionen ausgeben und erfahren, dass die IP-Adresse in Zeile 7, Position 14 liegt. setx ipaddr /F %temp%\tmp.dat /A 7, 14
6.2.3
Löschen von Variablen
Zum Löschen einer Variablen geben Sie den Namen gefolgt vom Gleichheitszeichen ein und drücken die Enter-Taste. Damit wird der Inhalt der Variablen und damit auch die Variable selbst gelöscht: set database=
Ein Setzen der Umgebungsvariablen mit SET erfolgt immer nur für die laufende Shell. Sind noch andere Fenster mit der Eingabeaufforderung geöffnet, übernehmen diese die Änderungen nicht. Möchten Sie Änderungen systemweit durchführen, existiert dafür seit Windows 2003 das Kommando setx. Ein mehrmaliges Löschen einer Variablen generiert übrigens keine Fehlermeldung. 19 Falls Sie neugierig geworden sind: Es funktioniert mit find und einer forSchleife, also sind Sie mit Windows 2000 nicht im Nachteil. Bevor Sie nachsehen, versuchen Sie doch, den Batch selbst zu bauen …
68
Arbeiten mit Variablen
6.2.4
Rechnen mit set (Option /a)
Die Option /a bewirkt eine Auswertung des Ausdrucks rechts vom Gleichheitszeichen als nummerischen Ausdruck. Damit sparen Sie sich in den meisten Fällen den Aufruf des Windows-Taschenrechners. Mögliche Operatoren für die Berechnung von Ausdrücken (in abnehmender Priorität): Operator
Bedeutung
()
– Gruppierung
* / % + -
– arithmetische Operatoren
<<
– bitweise Verschiebung um die angegebene Anzahl Bits
&
>> |
^
Tabelle 6.4: Die Liste der Operatoren für das Kommando set /a
Besitzt ein Zeichen des Ausdrucks eine besondere Bedeutung auf der Kommandozeile (wie z.B. das Zeichenpaar >>), so muss der Ausdruck komplett in doppelte Anführungszeichen eingeschlossen werden. Alle Berechnungen erfolgen mit Integer-Arithmetik, d.h., es werden keine Nachkommastellen verwendet. Ein set /a 14 / 3 ergibt als Ausgabe den Wert 4. Alle Zeichenfolgen im Ausdruck, die keine Zahlen oder Operatoren ergeben, werden als Namen von Umgebungsvariablen aufgefasst. Dies erspart Ihnen bei Berechnungen mit set /a das mühsame Einschließen der Variablennamen in Prozentzeichen. Die beiden folgenden Kommandos sind daher funktional völlig gleich, das zweite ist aber meiner Meinung nach leichter lesbar. set /A %disabled%=" %accountcontrol% & 0x02" set /A disabled=" accountcontrol & 0x02"
Konstante Zahlenwerte können (wie im obigen Beispiel zu sehen) in verschiedenen Zahlensystemen angegeben werden. Nummerische Werte stellen immer Dezimalzahlen dar, es sei denn, sie haben ein Präfix 0x für hexadezimale Zahlen oder 0 für oktale Zahlen. Damit stellt 0x12 dieselbe Zahl wie 18 oder 022 dar. Beachten Sie, dass die oktale Schreibweise verwirrend sein kann: So sind 08 und 09 keine gültigen Zahlen, da 8 und 9 keine erlaubten oktalen Ziffern sind20. Die Ausgabe erfolgt dagegen immer im Dezimalformat. 20 Aus diesem Grund sollten Sie auch niemals bei IP-Adressen führende Nullen angeben, viele andere Programme interpretieren diese Zahlen dann ebenfalls als oktal.
69
6 Umgebungsvariablen
Einen praktischen Einsatz findet set /a auch bei Arbeiten mit Bitflags. Im ActiveDirectory existiert für jeden Benutzer ein Attribut mit dem Namen userAccountControl. Die Werte dieses Attributs sind bitweise definiert. Das Bit mit dem Wert 2 beispielsweise legt fest, ob ein Account deaktiviert ist oder nicht. Um also einen Benutzer zu deaktivieren, müssen Sie nur den derzeitigen Wert auslesen (z.B. mit ldifde), dann per set /a den Wert 2 mit einem Oder verknüpfen und wieder zurückschreiben21. Im Kapitel 16.4.5 finden Sie die Vorstellung eines Shell Scripts, das genau diese Aufgaben erledigt.
6.2.5
Eingaben mit set (Option /p)
In vielen Fällen wird für die Ausführung eines Shell Scripts eine Eingabe vom Benutzer benötigt. Hierfür ist die Option /p gedacht. Dabei wird als Variableninhalt rechts vom Gleichheitszeichen die Meldung für den Benutzer ausgegeben, und die Eingabe des Benutzers wird anschließend der Inhalt der Variablen. Dazu ein Beispiel: set /p server=Bitte geben Sie den Servernamen ein:
Auf der Konsole wird dann der Text Bitte geben Sie den Servernamen ein: ausgegeben, und das System wartet auf eine Eingabe. Diese Eingabe kann natürlich auch per Umleitung aus einer Datei kommen. Die Variable server enthält anschließend die Eingabe des Benutzers. War die Variable vorher noch nicht belegt und der Benutzer gibt eine Leerzeile ein, wird die Variable nicht belegt. Weisen Sie daher der Variablen in einer Batchdatei immer einen Defaultwert zu, den Sie nach der Eingabemöglichkeit testen. So können Sie ohne Probleme feststellen, ob eine tatsächlich eine Eingabe erfolgt ist oder nicht. Bei einer Eingabeumleitung zusammen mit set /p wird die erste Zeile der Datei als Eingabezeile verwendet, die restlichen Zeilen werden ignoriert. Ein Pipelining funktioniert dagegen nicht. Hier als Beispiel eine kleine Datei (die auf der Autorenmaschine den Namen x.x trägt): Dies ist ein kurzer Text. Schon zu Ende ...
Zuerst versuche ich, ein Pipelining zu nutzen: type x.x | set /P zeile=Bitte eine Zeile eingeben:
Ein anschließendes set zeile liefert die Fehlermeldung „Umgebungsvariable zeile ist nicht definiert“. Klappt also nicht … 21 Oder Sie benutzen Windows 2003 und das Kommando dsmod …
70
Arbeiten mit Variablen
Nun versuche ich es mit Hilfe einer Eingabeumleitung: set /P zeile=Bitte eine Zeile eingeben: < x.x
Gebe ich nun set zeile ein, erhalte ich folgende Ausgabe: zeile=Das ist
Dieser Ansatz funktioniert ohne Probleme! Denken Sie daran, falls Sie Werte mit set über die Umleitung aus anderen Dateien setzen wollen22.
6.2.6
Ausgabe von Variableninhalten
Variablenexpansion Wird der Inhalt einer definierten Variablen für eine Ausgabe oder einen Befehl benötigt, so wird der Name der Variablen in Prozentzeichen eingeschlossen. Die Shell ersetzt dann bei der Ausführung des Kommandos diesen Ausdruck durch den Inhalt der Variablen. Der Befehl echo Die Zeit auf dem Rechner %computername% ist %time%
erzeugt so die Ausgabe (auf meinem Rechner): Die Zeit auf dem Rechner ORION ist 15:09:03,37
Ist die angegebene Variable nicht definiert, wird einfach der in Prozentzeichen eingeschlossene Name der Variablen ausgegeben und keine leere Zeichenkette! Dies ist bei der Erstellung von Batches zu beachten. Programmieren Sie eine entsprechende Abfrage, oder verwenden Sie das Kommando if defined für eine bedingte Ausführung des Befehls. Die Shell ersetzt Variablen normalerweise bei der Aktivierung eines Kommandos durch die jeweiligen Inhalte, nicht dynamisch zur Ausführungszeit. Wie Sie in Kapitel 8.1 erfahren, lassen sich mehrere Kommandos in einer Befehlszeile eingeben. Sobald Sie per Druck auf RETURN diese ausführen lassen, werden die Variablen ersetzt. Dies kann zu unerwünschten Resultaten führen. Wenn sie echo %time% & machwas.exe & echo %time% als Kommando ausführen lassen, wird zuerst die Zeit ausgegeben, dann machwas.exe ausgeführt, und danach wird wieder die Zeit ausgegeben. Trotzdem können Sie mit dieser Konstruktion nicht die Zeit für die Ausführung von machwas.exe messen, denn die Shell hat beide Variablen %time% bereits beim Start der Kommandokette expandiert, und somit erhalten Sie zweimal die gleiche Zeit. Als Lösung können Sie jeweils die Zeit mit Hilfe des Kommandos time /t ausgeben lassen, oder Sie nutzen die im nächsten Abschnitt
erläuterte verzögerte Auflösung von Variablen. 22 Ganz nebenbei ist dies eine vorzügliche Methode, um aus einer bestehenden Textdatei ohne jeden Aufwand die erste Zeile zu extrahieren.
71
6 Umgebungsvariablen
Verzögerte Variablenexpansion Der Weg aus dem obigen Dilemma nennt sich delayed expansion, also verzögerte Variablenerweiterung. Dies ist eine Funktion der Shell, die standardmäßig nicht aktiviert ist. Ein- und ausgeschaltet wird dies entweder durch die Startoption /V der Shell (s. Abschnitt 3.0.2) bzw. den Registry-Schlüssel DelayedExpansion (s. Abschnitt 3.2.1). Diese Funktion ist wohl aus gutem Grund nicht aktiviert, da sie für einen Einsteiger in die Windows Shell nicht ohne weiteres zu überblicken ist. Nach der Aktivierung der verzögerten Variablenexpansion kann der Name einer Variablen statt in Prozentzeichen auch in Ausrufezeichen eingeschlossen werden (z.B. !IPADDRESS!) und wird dann von der Shell zur tatsächlichen Ausführungszeit und nicht bei der Eingabe expandiert. Die Auswirkung möchte ich Ihnen am obigen Zeitmessungsbeispiel demonstrieren. Die Befehlszeile unten gibt die Zeit zum Zeitpunkt der Eingabe aus. Dann folgt eine Pause (Warten auf einen Tastendruck) und danach nochmals die Ausgabe einer Zeit2. echo %time% & pause & echo %time%
Wenn Sie diese Kommandozeile ausführen, erhalten Sie folgende Ausgabe23 (ganz egal, wie lange Sie auf einen Tastendruck warten): 1:46:43,90 Drücken Sie eine beliebige Taste ... 1:46:43,90
Alle zwei Ausgaben zeigen die gleiche Zeit an, da die Shell die Expansion der Variablen zur Zeit der Eingabe für den gesamten Eingabestring vornimmt. Jetzt starte ich eine neue Shell mit verzögerter Variablenauswertung (am einfachsten per DATEI/AUSFÜHREN und cmd /v:on) und gebe die Befehlszeile nochmals ein. Allerdings ersetze ich bei der ersten und zweiten Zeitausgabe die Prozentzeichen durch ein Ausrufezeichen, um die verzögerte Variablenexpansion zu nutzen. echo !time! & pause & echo !time!
Das ist die neue Ausgabe: 1:48:23,67 Drücken Sie eine beliebige Taste ... 1:49:05,53
Mit Hilfe dieser Syntax wird eine Variablen wirklich erst dann durch ihren Inhalt ersetzt, wenn das entsprechende Kommando auch ausgeführt wird. Ich weiß nicht, wie es Ihnen geht, aber dies ist das Verhalten, dass ich eigentlich als Standard von einer Shell erwarten würde … 23 Natürlich mit Ihrer Uhrzeit, nicht der hier abgedruckten.
72
Arbeiten mit Variablen
Damit lassen sich indirekte Referenzen auf Variablen auflösen. Eine indirekte Referenz ist eine Variable, die als Inhalt den Namen einer weiteren Variablen enthält, die dann den tatsächlichen Wert liefert. Bestimmte Aufgabenstellungen lassen sich nur so ohne großen Aufwand lösen: Für ein Projekt hatten wir eine schnelle Abfrage bestimmter Steuerparameter einer Maschine zu erstellen. Diese Parameter waren in verschiedenen Umgebungsvariablen gespeichert (einer der Projektmitarbeiter war ein eingefleischter Anhänger von Unix-Shells). Was wir brauchten, war also ein Shell Script (ein Batch) mit folgender Funktionalität: warten auf eine Benutzereingabe, Ausgabe in der Form „Variable x hat den Wert y“. Mit Hilfe der verzögerten Variablenexpansion ließ sich das in wenigen Zeilen lösen (hier ein vereinfachter Ausschnitt des Batches): set was= set /P was=Bitte Name der Variablen eingeben: if not defined was goto eof echo Der Inhalt von %was% = !%was%! :eof
Listing 6.1: Beispiel für eine verzögerte Variablenexpansion
Der interessante Teil ist die fett gedruckte Bezeichnung !%was%!. Was passiert an dieser Stelle? Zuerst wird beim Laden der Batchzeile sofort der Ausdruck %was% durch den Inhalt der Variablen ersetzt. Nehmen wir an, die Variable enthielt den Wert ipaddress. Also sieht die Zeile nun folgendermaßen aus: echo Der Inhalt von ipaddress = !ipaddress!
Durch die verzögerte Variablenexpansion wird nun bei der Ausführung des echo-Kommandos der Wert von ipaddress durch den Wert dieser Variablen ersetzt, und wir erhalten das gewünschte Ergebnis: Der Inhalt von ipaddress = 192.168.42.42
Insgesamt also eine sehr nützliche Einstellung, wenn auch etwas Einarbeitungszeit dazugehört. Zum Abschluss noch die Klärung einer Frage: Wie testen Sie eigentlich, ob die verzögerte Variablenexpansion eingeschaltet ist? Natürlich könnten Sie per Kommando reg einfach den entsprechenden Schlüssel in der Registry abfragen, es geht aber auch mit einer Zeile in der Shell: set was=123 if !was! == %was% echo Delayed Expansion ON!
Ist die verzögerte Auswertung nicht eingeschaltet, dann bleibt !was! einfach als Textausdruck stehen, während %was% zu 123 erweitert wird. Erweiterte Variablenexpansion Nicht in allen Fällen ist eine einfache Ersetzung des Platzhalters durch den Inhalt der Variablen ausreichend. So ist es beispielsweise oft wünschenswert, Leerzeichen aus dem Inhalt zu entfernen, nur einen Teil des Variablenwertes zu übernehmen oder anderweitige
73
6 Umgebungsvariablen
Umformungen vorzunehmen. Bei der Ausgabe aus dem vorherigen Abschnitt stört mich beispielsweise die Angabe der Hundertstelsekunden (so schnell tippe ich mit meinem 6-Finger-System auch wieder nicht, als dass ich diese Angabe wirklich brauchen würde). Mit den Befehlserweiterungen der Windows Shell ist dies kein Problem. Um dieses besondere Verhalten zu aktivieren, wird innerhalb der Prozentzeichen nach dem Namen der Variablen ein Doppelpunkt gesetzt, dem die gewünschte Operation folgt. Tabelle 6.5: Möglichkeiten der erweiterten Variablenersetzung
%var:x=y%
ersetzt jedes Auftreten des Strings x durch den String y
%var:x=%
löscht jedes Auftreten des Strings x
%var:*x=y%
ersetzt alles bis zum Auftreten von String x durch den String y
%var:~n,m%
gibt ab dem Offset n (beginnt bei 0!) genau m Zeichen aus, negative Zahlen zählen ab dem Ende
%var:~n%
gibt ab dem Offset n (beginnt bei 0!) den Rest der Zeichen aus, negative Zahlen zählen ab dem Ende
Damit können Sie das Kommando zur Zeitausgabe einfach erweitern: echo Die Zeit auf dem Rechner %computername% ist %time:~0,-3%
Das erzeugt so die Ausgabe (auf meinem Rechner): Die Zeit auf dem Rechner ORION ist 15:09:03
Denken Sie auch daran, dass diese Veränderungen nur für die Ausgabe des Variableninhalts an der Stelle dieses Platzhalters gelten, der tatsächliche Inhalt der Umgebungsvariablen wird davon nicht berührt. Damit sind Sie in der Lage, alle notwendigen Manipulationen an Variableninhalten durchzuführen, die für komplexe Befehlsabläufe benötigt werden. %time:~6,2% liefert beispielsweise die aktuellen Sekunden der Systemuhr für die automatische Vergabe von Dateinamen. Um aus einem Variableninhalt alle Leerzeichen zu entfernen, verwenden Sie den Ausdruck %varname: =%. Mit einer kreativen Anwendung der erweiterten Ersetzung sind auch fortgeschrittene Umformungen kein Problem mehr. Angenommen, in der Variablen server ist der vollqualifizierte DNS-Name eines Rechners gespeichert: set server=kiste.firma.de
Alles, was Sie für die Umwandlung in einen LDAP-String benötigen, ist eine einzige Zeile: echo cn=%server:.=,dc=%
Alle Vorkommen des Punkts werden durch die Zeichenkette ,dc= ersetzt. Hier die Ausgabe: cn=kiste,dc=firma,dc=de
74
Arbeiten mit Variablen
Mehrfaches Verwenden dieser Funktionen in einer Ersetzung sind nicht möglich. Weisen Sie das Ergebnis dieser Ersetzung einer Zwischenvariablen zu, und bearbeiten Sie diese dann weiter.
6.2.7
Die Variable errorlevel
Jedes Kommando, das von der Shell gestartet wird, wird üblicherweise irgendwann beendet. Das Ende des Kommandos oder des externen Prozesses liefert einen Code an die Shell zurück, mit dem der Erfolg oder eine bestimmte Fehlerbedingung signalisiert werden kann. Dieser Code wird als Exit-Code oder errorlevel bezeichnet und in der vordefinierten Variablen errorlevel gespeichert. So kann per Shell jederzeit festgestellt werden, ob ein Kommando erfolgreich abgearbeitet wurde oder nicht. Natürlich benötigen Sie dazu eine Dokumentation über die einzelnen Fehlercodes für den jeweiligen Prozess oder das Kommando. Innerhalb der Windows-Welt hat sich hierbei eine gewisse Konvention herausgebildet: Alle „erfolgreichen“ Aktionen liefern einen erorrlevel von 0 zurück. Ein von 0 verschiedener Wert bedeutet eine Warnung oder einen Fehler. Was nun „erfolgreich“ bedeutet, wird von den Entwicklern externer Kommandos und Applikationen definiert. So liefert das find-Kommando einen Errorlevel von 0 bei einer Übereinstimmung mit dem Suchstring und 1, wenn nichts gefunden wurde. Das Kommando xcopy.exe liefert den Errorlevel 4, wenn die Datei nicht gefunden wurde, und 1, wenn eine Datei auf sich selbst kopiert werden sollte. Für die meisten Applikationen und Tools finden Sie die Werte in der Dokumentation unter den Stichpunkten „error codes“ oder „exit codes“.
6.2.8
setlocal & endlocal
Innerhalb von Shell Scripts werden oft Änderungen an Umgebungsvariablen durchgeführt. Viele dieser Änderungen werden nur während der Laufzeit des Shell Scripts benötigt und müssten im Normalfall am Ende eines Batches aufwändig wieder rückgängig gemacht werden. Daher existiert seit Windows 2000 die Möglichkeit, mit Hilfe von setlocal den Gültigkeitsbereich von Änderungen am Environment auf die laufende Batchdatei zu beschränken. Bei Ende des Batches muss dann wieder mit Hilfe von endlocal diese Einschränkung aufgehoben werden. Falls kein endlocal erfolgt, führt die Batch-Engine implizit für jede noch offene setlocal ein endlocal aus. Achten Sie aus Gründen der Wartbarkeit aber dennoch darauf, für jedes setlocal in Ihren Shell Scripts auch ein entsprechendes endlocal zu schreiben.
75
6 Umgebungsvariablen
76
7
Kommandoausführung
7.1
Ausführbarkeit von Dateien
7.1.1
Ausführbare Dateien mit pathext definieren
Wie stellt die Shell überhaupt fest, ob ein Kommando ausführbar ist bzw. welches Programm bei der Eingabe eines nicht ausführbaren Kommandos gestartet werden soll? Unter Windows wird für diese Kennzeichnung die Erweiterung einer Datei verwendet, also der Teil des Dateinamens nach dem letzten Punkt. In anderen Betriebssystemen existiert keine solche Unterscheidung, sondern eine Datei wird einfach als „ausführbar“ markiert (so z.B. in allen Unix-artigen Betriebssystemen). Die Erweiterungen aller direkt ausführbaren Dateien werden in der Umgebungsvariablen pathext abgelegt. Mehrere Einträge werden dabei durch ein Semikolon getrennt. Hier der Inhalt dieser Variablen nach einer Standardinstallation: PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH
Alle Dateien, die eine der aufgelisteten Erweiterungen tragen, können durch die Angabe ihres Dateinamens in der Shell direkt ausgeführt werden, die Angabe der Erweiterung ist nicht notwendig. Genau hier liegt aber meiner Meinung nach ein potenzielles Sicherheitsrisiko: Wie aus der Ausgabe ersichtlich, werden auch alle Dateien des Windows Script Host (.vbs, .js, .vbe, .jse, .wsf und .wsh) einfach durch die Angabe des Namens ausgeführt. Ein kleines Beispielszenario verdeutlicht meine Bedenken gegenüber dieser Standardeinstellung: Ich möchte in der Shell nur schnell den Inhalt einer Variablen überprüfen. Gleichzeitig liegt auf meiner Maschine im aktuellen Verzeichnis oder im Suchpfad eine Datei mit dem Namen eco.vbs, die mir ein netter Zeitgenosse dort abgelegt hat. Statt des gewünschten Kommandos echo verschreibe ich mich und tippe nur eco %time%. Als Ergebnis führt das System die Datei eco.vbs aus, mit allen möglichen Konsequenzen! Daher empfehle ich Ihnen, aus der Systemvariablen pathext (per SYSTEMEIGENSCHAFTEN oder setx) die Scripterweiterungen zu entfernen. Alle VB-Scripte für den WSH bleiben natürlich immer noch nutzbar, aber ich muss jetzt die Erweiterung (z.B. .vbs) mit angeben und weiß damit, dass ich ein ActiveX-Script starte. Auf jeden Fall
77
7 Kommandoausführung
kann ich dies nicht mehr unabsichtlich durch einen kleinen Vertipper tun. Durch die Dateinamenvervollständigung ist dies auch nicht mehr Schreibarbeit, aber mein System ist etwas fehlertoleranter. Andererseits kenne ich eine Reihe von Administratoren, die in diese Liste der Erweiterungen in pathext die Erweiterung .msc aufgenommen haben. Dies sind die abgespeicherten Managementkonsolen. Nach dieser Änderung kann z.B. ACTIVE VERZEICHNIS USERS AND COMPUTERS einfach durch die Eingabe dsa gestartet werden. Sehr praktisch …
7.1.2
assoc & ftype
Was passiert, wenn ich einen Dateinamen als Kommando eingebe, dessen Erweiterung nicht in der Liste der ausführbaren Erweiterungen vorhanden ist? Dann wird das damit verknüpfte Programm gestartet. Für alle Dateien mit der Erweiterung .txt ist dies standardmäßig notepad.exe, der Editor. In der GUI ist diese Verknüpfung relativ einfach zu kontrollieren: Über den Explorer lässt sich mit EXTRAS/ORDNEROPTIONEN im Reiter DATEITYPEN diese Einordnung nachsehen und ändern. Aber auch auf Shell-Ebene existieren Kommandos für die Verwaltung dieser Zuordnung. Microsoft hat diese Funktionalität allerdings nicht ganz optimal implementiert. Es existieren zwei Kommandos, eines für die Verwaltung der registrierten Dateitypen (assoc) und ein zweites (ftype) für die Zuordnung des zu startenden Programms. Das Kommando assoc sucht für eine angegebene Erweiterung den registrierten Dateityp heraus und gibt diesen auf der Konsole aus: C:\tmp>assoc .bmp .txt=Paint.Picture
Wird keine Erweiterung angegeben, gibt assoc eine Liste aller registrierten Dateierweiterungen des Systems und der damit verknüpften Dateitypen aus. Das Kommando kann aber auch in der Form assoc .erw=dateityp verwendet werden, um die Zuordnung zu ändern. Erfolgt keine Angabe eines Dateityps, wird die Zuordnung der Erweiterung gelöscht. Damit haben wir aber erst einen Teil der Aufgabe gelöst, immerhin wissen wir nun, dass eine .bmp-Datei ein „Paint.Picture“ ist. Für den Rest ist das Kommando ftype zuständig. Dieses Kommando erwartet die Angabe eines Dateityps und gibt den zugeordneten Handler aus, also das Programm, das tatsächlich gestartet wird. Sehen wir uns die zum obigen Beispiel passende Ausgabe an: C:\tmp>ftype Paint.Picture Paint.Picture="C:\WINNT\System32\mspaint.exe" "%1"
78
Das Kommando start
Auch hier wird wieder die komplette Liste aller Handler ausgegeben, wenn beim Aufruf kein Dateityp angegeben wird. Sie wollen wissen, für welche Dateitypen auf Ihrem System der Internet Explorer zuständig ist? Kein Problem: C:\tmp>ftype | find /i "iexplore.exe" Adobe.SVGCtl="C:\Programme\IE\iexplore.exe" –nohome (viele Zeilen gelöscht) : xmlfile="C:\Programme\IE\iexplore.exe" –nohome xslfile="C:\Programme\IE\iexplore.exe" -nohome
Ich habe die Liste hier aus Platzgründen gekürzt. Durch die Kombination mit dem Filterkommando find ist es einfach, alles Vorkommen der Datei iexplore.exe (dem IE) herauszufiltern. Mehr Informationen zu den Filtern finden Sie im Kapitel 4.3.2, dort wird find ausführlich behandelt. Sicherlich denken Sie jetzt auch: „Wäre ja toll, wenn das in einem Aufruf ginge, anstatt nacheinander zwei Befehle zu benutzen und sich eine Ausgabe auch noch zu merken.“ Sie haben Recht, es ist wesentlich praktischer und ein schönes Beispiel, wie sich Batches, Makros und andere Konzepte sinnvoll einsetzen lassen. Im Kapitel über die Nutzung von for finden Sie die Lösung zu diesem Problem (Abschnitt 9.2.8). ftype kann aber auch genau wie assoc als Zuweisungskommando benutzt werden. Setzen Sie dazu hinter den Dateityp ein Gleichheitszeichen gefolgt vom kompletten Pfadnamen des auszuführenden Programms. Dies entspricht der Änderung im Windows Explorer über EXTRAS/ORDNEROPTIONEN und DATEITYPEN. Wenn Sie dort die Schaltfläche ERWEITERT anklicken, können Sie dort die Zuordnung für das open-Ereignis einstellen. Ein schönes Beispiel, wie effizient die Arbeit mit der Shell sein kann.
7.2
Das Kommando start
Das Kommando start entspricht nur ungefähr der Funktion von START/AUSFÜHREN. Darüber hinaus können Prozesse mit anderer Priorität gestartet, URLs oder Verzeichnisse geöffnet werden, und es kann auf das Ende des gestarteten Prozesses gewartet werden. Hier die komplette Syntax für start: START ["Titel"] [/Dpath] [/I] [/MIN] [/MAX] [/SEPARATE | /SHARED] [/LOW | /NORMAL | /HIGH | /REALTIME] | / ABOVENORMAL | /BELOWNORMAL] [/WAIT] [/B] [Befehl/Programm] [Parameter]
Die folgende Tabelle listet die Bedeutung der einzelnen Optionen für das Kommando start auf.
79
7 Kommandoausführung Tabelle 7.1: Optionen für das start-Kommando
Option
Bedeutung
"Titel"
Der Titel des neuen Fensters.
Pfad
Startverzeichnis.
B
Startet Anwendung, ohne ein neues Fenster zu öffnen. Die Anwendung ignoriert (STRG)+(C). Wenn die Anwendung nicht selbstständig (STRG)+(C) überprüft, ist (STRG)+(UNTBR) die einzige Möglichkeit, um die Anwendung abzubrechen.
I
Die CMD.EXE beim Aufruf übergebene Umgebung soll die neue aktuelle Umgebung sein.
MIN
Startet das Fenster minimiert.
MAX
Startet das Fenster maximiert.
SEPARATE
Startet 16-Bit-Windows-Programm in separatem Speicherbereich.
SHARED
Startet 16-Bit-Windows-Programm im gemeinsamen Speicherbereich.
LOW
Startet in IDLE-Prioritätsklasse.
NORMAL
Startet in der NORMAL-Prioritätsklasse.
HIGH
Startet in der HIGH-Prioritätsklasse.
REALTIME
Startet in der REALTIME-Prioritätsklasse.
ABOVENORMAL
Startet in der ABOVENORMAL-Prioritätsklasse.
BELOWNORMAL
Startet in der BELOWNORMAL-Prioritätsklasse.
WAIT
Startet die Anwendung und wartet auf das Ende.
Befehl/Programm
Wenn ein interner Befehl oder eine Batchdatei verwendet wird, wird das neue CMD-Fenster mit Parameter /K gestartet. Das bedeutet, dass das Fenster erhalten bleibt, nachdem der Befehl ausgeführt wurde. Wenn dies nicht der Fall ist, wird das Programm entweder als Anwendung in einem Fenster oder als Konsolenanwendung gestartet.
Parameter
Parameter, die an den Befehl oder das Programm übergeben werden sollen.
Erst mit Hilfe dieses Kommando erhalten Sie also die komplette Kontrolle über gestartete Prozesse! Diese Optionen finden Sie nicht im GUI, daher ist dies auch die einzige Möglichkeit für Benutzer, ein Programm zu starten, das mehr CPU-Leistung als andere Programme erhält. Ich empfehle Ihnen aber, die Prioritätsklasse „High“ nur mit Bedacht und „Realtime“ gar nicht einzusetzen. Mit „High“ läuft der Task-Manager und mit „Realtime“ Teile des Betriebssystems. Falls Sie sich also prozesstechnisch in die Ecke gepinselt haben, ich hatte Sie gewarnt … Noch mehr Details zu den einzelnen Optionen erhalten Sie, wenn Sie start einfach mit der Option /? aufrufen.
80
Das Kommando runas
7.3
Das Kommando runas
Falls Sie gerade vor einem Computer sitzen, wie sind Sie angemeldet? Als ganz normaler Benutzer oder mit administrativen Rechten? Was könnte im schlimmsten Fall passieren? Aus diesen Gründen haben mittlerweile fast alle Administratoren mehr als einen Account: einen ganz normalen für die tägliche Arbeit mit Office & Co. und mindestens einen weiteren, administrativen Account für Verwaltungsaufgaben. Viele Administratoren wechseln zwischen diesen beiden immer noch durch Ab- und Anmelden hin und her. Viel einfacher geht dies aber mit Hilfe des Kommandos runas, das dem Unix-Kommando su nachempfunden wurde. Hier wird ein Prozess mit den angegebenen Anmeldeinformationen gestartet, ohne dass Sie Ihre Sitzung unterbrechen müssen. Der einfachste Aufruf sieht wie folgt aus: RUNAS /user: Programm
Öffnen Sie sich doch einfach damit eine Administrator-Shell mit dem Kommando runas /user:<usr> "%systemroot%\system32\cmd.exe"
Alle Kommandos, die Sie aus dieser Shell starten, laufen im Kontext des administrativen Kontos, und Sie ersparen sich unnötiges An- und Abmelden. Damit Sie diese Shell nicht mit einer „normalen“ verwechseln, sollten Sie diese entsprechend kennzeichnen. Ich stelle dazu meist den Titel des Shell-Fensters um und wähle eine entsprechend markante Farbkombination für Vordergrund und Hintergrund. Damit fällt mir schnell auf, dass ich die Befehle nicht in der „Normaler-Benutzer-Shell“ eingebe. Tipps dazu finden Sie im Kapitel „Laufzeitkonfiguration“ (Kapitel 3.3). Natürlich können Sie beide Methoden (start und runas) kombinieren, indem Sie mit runas ein entsprechendes start-Kommando absetzen. Auch das Windows GUI unterstützt übrigens mit dem Explorer das Ausführen von Programmen in einem anderen Security-Kontext. Dazu halten Sie einfach eine der beiden (ª)-Tasten gedrückt und klicken mit der rechten Maustaste auf das Programm. Im Kontextmenü erscheint dann ein Eintrag AUSFÜHREN ALS …, mit dem Sie die gewünschten Benutzerdaten auswählen können. Dies erspart langwieriges Ab- und Anmelden, gerade bei Supporteinsätzen am Computer des Benutzers.
81
7 Kommandoausführung
7.4
Prozessverwaltung mit der Shell
Jeder Benutzer kennt sicherlich den Task-Manager, mit dem Prozesse oder widerspenstige Programme beendet und die Systemleistung verfolgt werden können. Es gibt aber auch die Möglichkeit der Prozessverwaltung per Kommandozeile, die einige Möglichkeiten bietet, die im Task-Manager nicht enthalten sind. Gerade für Administratoren, die regelmäßig auch auf entfernten Systemen beobachten müssen, welche Prozesse laufen, kann dies eine große Arbeitserleichterung sein.
7.4.1
tlist oder tasklist?
Für die Verwaltung von Prozessen stehen zwei unterschiedliche Kommandosets zur Verfügung. Bis zu Windows 2000 existieren in den Support Tools die beiden Kommandos tlist und kill. Ab Windows XP heißen diese beiden Kommando tasklist und taskkill. Dies wäre an sich nur ein kleineres Problem, allerdings unterscheiden sich tlist und tasklist auch in ihrer Funktionalität. Dies vor allem so, dass in jedem Kommando Möglichkeiten enthalten sind, die das andere nicht hat. Auch unter Windows 2003 lassen sich die Support Tools von Windows 2000 verwenden, und tlist.exe ist eines der Programme, die auch unter Windows 2003 gute Dienste leisten, wie die folgenden Abschnitte zeigen.
7.4.2
Prozessinformationen
Jeder Prozess in Windows wird durch seine Prozess-ID (pid) identifiziert. Der Name des Prozesses kann also durchaus öfter auftreten, wenn Sie zum Beispiel mehrere Notepad-Fenster öffnen.
7.4.3
tlist
Der Aufruf von tlist ohne Parameter liefert eine einfache Prozessliste analog der Auflistung im Task-Manager. Listing 7.1: Beispielausgabe von tlist
82
C:\Buecher\Shell-Buch>tlist 0 System Process 8 System 152 SMSS.EXE 192 CSRSS.EXE 212 WINLOGON.EXE NetDDE Agent 240 SERVICES.EXE 252 LSASS.EXE 436 svchost.exe 464 spoolsv.exe 620 svchost.exe 648 mdm.exe
Prozessverwaltung mit der Shell 724 764 848 980 1008 1288 1164 1316 1344 1348 1404 1408 1488 1248 444
Program Manager Avance Sound Effect Control Panel FreePDF_Assistant
ISDNWatch Shellbuch.doc - Microsoft Word C:\WINNT\system32\cmd.exe - tlist
Sie sehen eine Liste aus pid, Prozessname und Fenstertitel, die der Ausgabe im Task-Manager ähnelt. tlist kann allerdings noch mehr: Wenn Sie eine gültige pid als Argument angeben, liefert tlist Detailinformationen zum angegebenen Prozess. C:\Buecher\Shell-Buch>tlist 1248 1248 CMD.EXE C:\WINNT\system32\cmd.exe - tlist 1248 CWD: C:\Buecher\Shell-Buch\ CmdLine: "C:\WINNT\system32\cmd.exe" VirtualSize: 11432 KB PeakVirtualSize: 11496 KB WorkingSetSize: 1120 KB PeakWorkingSetSize: 1124 KB NumberOfThreads: 1 1136 Win32StartAddr:0x4ad1a4b0 LastErr:0x000000cb State:Waiting 5.0.2195.6824 shp 0x4ad00000 cmd.exe 5.0.2195.6899 shp 0x77880000 ntdll.dll 5.0.2195.6897 shp 0x77e70000 KERNEL32.dll 5.0.2195.6897 shp 0x77e00000 USER32.dll 5.0.2195.6898 shp 0x77f40000 GDI32.DLL 5.0.2195.6876 shp 0x79350000 ADVAPI32.dll 5.0.2195.6802 shp 0x77d20000 RPCRT4.DLL 6.1.9844.0 shp 0x78000000 MSVCRT.dll
Listing 7.2: Detailinformationen zum Prozess cmd.exe
Anhand dieser Liste kann ein Administrator oder Betreuer für jeden laufenden Prozess die exakte Version einer geladenen DLL feststellen; ob eine DLL, die ersetzt werden soll, noch im Speicher liegt; wie viel Speicher der Prozess belegt und vieles mehr. Über den Schalter -m und die Angabe einer DLL lässt sich sogar mit nur einem Kommando herausfinden, welche Prozesse die angegebene DLL referenzieren: H:\Armin\Buecher\Shell-Buch>tlist -m wininet.dll WININET.dll - 1008 svchost.exe WININET.dll - 1288 explorer.exe Program Manager WININET.DLL - 1148 ccApp.exe
Listing 7.3: Wer benutzt die wininet.dll?
83
7 Kommandoausführung tlist kann helfen, ein weiteres Rätsel zu lösen. Im Task-Manger finden
Sie nach einem Rechtsklick auf einen Prozess nicht nur die Auswahl PROZESS BEENDEN, sondern auch PROZESSSTRUKTUR BEENDEN (end process tree bei einem englischen Windows). Wie stellen Sie aber fest, welche Prozesse von diesem Prozess gestartet wurden? Ganz einfach, über die Option -t von tlist. Ein Beispiel der Ausgabe von einem meiner Rechner; aus Platzgründen habe ich die Liste etwas gekürzt. Listing 7.4: Ausgabe des Prozessbaumes
Man erkennt leicht, dass das System den Session Manager (smss.exe) und dieser das Windows-Subsystem (csrss.exe), den Logon-Dienst (winlogon.exe) und die lokale Sicherheitsautorität (lsass.exe) gestartet hat. Für mein Benutzerkonto sehe ich, dass der Explorer als Startprozess läuft und unter dessen Kontrolle zwei Instanzen der Shell gestartet wurden (pid 1248 und pid 660). In einer der Shells läuft ftp (pid 1252), aus dem ich per !cmd eine weitere Shell und von da aus Notepad gestartet habe. Wird nun der Prozessbaum für die pid 1444 beendet, betrifft dies auch den Notepad-Prozess mit der pid 164, da dieser von Prozess 1444 gestartet wurde.
84
Prozessverwaltung mit der Shell
Es gibt noch eine Reihe weiterer Optionen wie die Auflistung aller in einem Prozess laufenden Services oder die Suche nach Prozessnamen mit einem regulären Ausdruck. Ich überlasse Ihnen das als Übung und bin mir sicher, dass auch auf Ihrem System tlist noch eine Zukunft hat.
7.4.4
tasklist
tasklist die Variante für Windows XP und Windows 2003, die tlist eigentlich ablösen sollte. Da die Ausgabe des Prozessbaumes
und einige andere Dinge allerdings in dieser Version fehlen, sind bei mir noch beide Versionen im Einsatz. In der Ausgabe unten finden Sie die Ausgabe eines Aufrufs von tasklist: Image Name Session# Mem Usage ========================= =========== ============ System Idle Process 0 16 K System 0 216 K smss.exe 0 480 K csrss.exe 0 4,016 K winlogon.exe 0 6,220 K services.exe 0 4,740 K lsass.exe 0 29,384 K svchost.exe 0 2,676 K svchost.exe 0 3,620 K svchost.exe 0 3,968 K svchost.exe 0 2,024 K svchost.exe 0 15,092 K spoolsv.exe 0 4,668 K msdtc.exe 0 3,796 K dfssvc.exe 0 4,448 K dns.exe 0 5,680 K svchost.exe
K 1484 Console K 1496 Console K 1612 Console K 412 Console K 880 Console K 984 Console K 2204 Console K 2428 Console K 1012 Console K 180 Console K
Im Gegensatz zu tlist hat sich Microsoft bei tasklist einige Gedanken gemacht, wie die Informationen sinnvoll für eine Ausgabe aufbereitet werden können. Zusätzlich lassen sich mehr Daten abfragen als bei tlist. Für die Ausgabe wäre es ziemlich nachteilig, wenn alle Daten nur spaltenweise ausgegeben würden. Eine detaillierte Prozessinformation für die Zeichentabelle charmap.exe sieht beispielsweise so aus: Image Name PID Session Name Session# Mem Usage Statu s User Name CPU Time Windo w Title ========================= ====== ================ =========== ============ ===== ========== ================================================== ============ ===== ======================================================== =========== charmap.exe 2072 Console 0 3,204 K Runni ng SHELLBOOK\Administrator 0:00:00 Chara cter Map
Nicht unbedingt lesbar, oder? Daher existiert eine Option /FO (für format), bei der Sie angeben können, ob Sie die Ausgabe als Tabelle wie oben (table), als kommaseparierte Liste (csv) oder als Liste (list) erhalten wollen. Unten sehen Sie eine Ausgabe als Liste, was die Informationen wesentlich leichter erfassbar macht:
86
Prozessverwaltung mit der Shell Image Name: PID: Session Name: Session#: Mem Usage: Status: User Name: CPU Time: Window Title:
charmap.exe 2072 Console 0 3,204 K Running SHELLBOOK\Administrator 0:00:00 Character Map
Das Kommando dafür lautet tasklist
/FI "imagename eq charmap.exe" /FO list /v
Hier sehen Sie auch gleich einen wichtigen Unterschied: Für die Auswahl eines bestimmten Prozesses existiert nun ein Filterausdruck, mit dem auch komplexe Auswahlen möglich sind, z.B. alle nicht reagierenden Programme. Filtername
Operatoren
Gültige Werte
STATUS
eq, ne
running, not responding, unknown
IMAGENAME
eq, ne
Name des Programms
PID
eq, ne, gt, lt, ge, le
Prozess-ID
SESSION
eq, ne, gt, lt, ge, le
Sitzungsnummer
SESSIONNAME
eq, ne
Sitzungsname
CPUTIME
eq, ne, gt, lt, ge, le
CPU-Zeit im Format hh:mm:ss
MEMUSAGE
eq, ne, gt, lt, ge, le
Speicher in KByte
USERNAME
eq, ne
Benutzer in der Form [domain\] user
SERVICES
eq, ne
Servicename
WINDOWTITLE
eq, ne
Fenstertitel
MODULES
eq, ne
Name der DLL
Tabelle 7.2: Filteroptionen für tasklist
Endlich kann mit diesem Kommando auch ohne Probleme ein entferntes System abgefragt werden, auch mit einem anderen Benutzerkontext, ohne erst runas (s. Seite 81) bemühen zu müssen. Option
Bedeutung
/S <system>
Gibt den Namen des Remote-Systems an.
/U [domain\] user
Benutzerkontext, unter dem das Kommando ausgeführt wird.
/P <password>
Passwort für den angegebenen Benutzer, wird abgefragt, falls diese Option nicht angegeben wird.
/M <module>
Listet alle Prozesse auf, die die angegebene DLL verwenden. Wird nur /M angegeben, werden alle DLLModule gelistet.
Tabelle 7.3: Befehlsoptionen für tasklist
87
7 Kommandoausführung
Option
Bedeutung
/SVC
Zeigt die in den Prozessen laufenden Dienste an.
/V
Zeigt ausführliche Prozessinformationen an.
/FI
Definiert den Filter für die Anzeige (s.o.).
/FO
Definiert das Ausgabeformat. Gültige Werte sind die drei Zeichenketten "table", "list" oder "csv".
/NH
Unterdrückt die Ausgabe der Spaltentitel. Gilt nicht für das Ausgabeformat "list".
Möchten Sie alle Prozesse auf dem Rechner kiste sehen, die nicht vom System gestartet wurden, geben Sie folgendes Kommando ein: Eine Zeile
tasklist /s kiste /fo list /fi "username ne NT AUTHORITY\*" | find /i "Image name:"
Dieses Kommando benutzt als Ausgabeformat eine zeilenweise Liste (/fo list) und filtert nach dem Attribut username, das nicht mit einem Prozess des Betriebssystems (NT AUTHORITY\*) übereinstimmen darf. Da ich nicht an Prozess-ID, Speicherbedarf oder anderen Daten interessiert bin, wird die Ausgabe zum Schluss noch vom find-Filter bearbeitet.
7.4.5
kill und taskkill
Hit Hilfe eines dieser Kommandos kann ein Prozess beendet werden. kill erwartet die Angabe einer Prozess-ID oder eines Prozessnamens. Wird vor der pid als Option -f (für force) angegeben, erzwingt dies die sofortige Beendigung des Prozesses (Vorsicht! Datenverlust möglich). Alternativ kann für kill anstelle eines eindeutigen Prozessnamens auch ein regulärer Ausdruck als Suchmaske angegeben werden. kill beendet dann alle Prozesse, deren Name auf die Suchmaske zutrifft! Kombinieren Sie ein "*" mit der Option –f, und Sie lernen schnell, was der Ausdruck „den Ast, auf dem man sitzt, absägen“ bedeutet … Bei taskkill gelten die gleichen Optionen und Filtermöglichkeiten wie bei tasklist. Allerdings lässt sich das System hier nicht so leicht in den Abgrund reißen: Die Option /IM * (für image name all) ist nur dann erlaubt, wenn zusätzlich mit /FI ein Filter angegeben wird. Das gewaltsame Ende eines Prozesses wird hier aus Gründen der Konsistenz mit /F (statt -f bei kill) erzwungen. Prozesse auf entfernten Systemen werden immer mit dieser Option beendet. Seien Sie also vorsichtig, bevor Sie Ihrem Kollegen das Word beenden, in dem er seit drei Stunden ohne Sicherung vor sich hin tippt …
88
8
Befehlsverknüpfung
Der Exit-Code eines Prozesses kann benutzt werden, um zu steuern, ob ein weiteres Kommando ausgeführt werden soll oder nicht. So lassen sich Befehle erstellen, die in Abhängigkeit des Errorlevels bestimmte Aufgaben ausführen oder nicht. Realisiert wird dies mit der Shell über bestimmte Operatoren, welche die einzelnen Elemente des Befehls trennen.
8.1
Mehrfachkommandos
Die einfachste Form der Verknüpfung von Kommandos ist das lineare Abarbeiten von Befehlen. Dazu verbinden Sie die einzelnen Kommandos einfach mit einem einfachen kaufmännischen Und-Zeichen („&“). cls & machwas.exe & copy datei.ext ziel.dat & nochwas.bat & echo Fertig.
Das System wird nun der Reihe nach alle Kommandos ausführen. Falls eines der Kommandos in der Befehlskette fehlschlägt, wird mit dem nächsten Kommando in der Liste weitergemacht. Damit erschöpft sich die Nutzbarkeit meiner Meinung nach auf eine einfache Aneinanderreihung kleiner Sequenzen, für die eine Batchdatei zu viel Aufwand wäre. Störend ist allerdings die fehlende Möglichkeit der Fehlerbehandlung. In vielen Fällen nämlich hängt ein späteres Kommando davon ab, ob ein vorhergehender Befehl erfolgreich ausgeführt wurde. Diese Möglichkeiten haben Sie mit den nachfolgenden Operatoren.
8.2
Der Operator &&
Wird das kaufmännische Und einfach verdoppelt, wird das folgende Kommando nur dann ausgeführt, wenn der erste Befehl erfolgreich war. Wie stellt das System nun fest, ob ein Kommando erfolgreich war? Mit Hilfe des Errorlevels (s. Abschnitt 6.2.7), für den ein erfolgreiches Kommando durch einen Errorlevel von 0 definiert ist. MakeCSV.exe datei.ext && csvde -i -f datei.ext
Das obige Beispiel führt einen ActiveDirectory-Import über das Tools csvde nur dann aus, wenn der (fiktive) Prozess MakeCsv.exe mit einem Errorlevel von 0 geendet hat.
89
8 Befehlsverknüpfung
Die Anzahl der nacheinander folgenden Kommandos wird nur durch die Länge der Befehlszeile begrenzt, die bei Windows 2000 und aufwärts bei knapp zwei Kilobyte liegt, genug Platz also für längere Kommandos.
8.3
Der Operator ||
Ich bin ein Anhänger von fehleraktivierten Systemen. Dies bedeutet, dass nicht dauernd ein grünes Licht brennt, das „Alles o.k.“ signalisiert, sondern alles ist dunkel. Bei Problemen erscheint dann ein rotes Licht. So ein Ansatz spart Aufmerksamkeit. Mit Hilfe des &&-Operators lässt sich dieser Ansatz aber nicht realisieren. Daher existiert auch noch der ||-Operator. Dieser sorgt dafür, dass ein nachfolgendes Kommando nur dann ausgeführt wird, wenn das vorhergehende Kommando fehlschlägt. Als Fehlschlag ist ein Errorlevel ungleich 0 definiert. Gerade in Batchfiles macht sich die einfachere Notation bemerkbar. So müssen Sie nicht mehr in einer eigenen Zeile prüfen, sondern können eine eventuell notwendige Fehlerbehandlung gleich hinter das Kommando schreiben: MakeCSV.exe datei.ext || echo Probleme!
Unten sehen Sie zum Vergleich einen äquivalenten Ausschnitt aus einer Batchdatei: MakeCSV.exe datei.ext if errorlevel 1 echo Probleme!
Jetzt habe ich nur noch einen Wunsch: Das Kommando soll zwar im Fehlerfall einen besonderen Befehl ausführen (hier unser echo Probleme!), danach aber mit einem weiteren Kommando fortfahren. Auch dies ist mit der Shell ohne Probleme realisierbar, wie der nächste Abschnitt zeigt.
8.4
Befehlsgruppierung
Mit Hilfe der Möglichkeit der Befehlsgruppierung lassen sich auch sehr komplexe Abläufe innerhalb eines Kommandos erstellen. Genau wie in der Mathematik dienen auch hier die runden Klammern als Gruppierung für die Auswertungsreihenfolge. Jedes Klammerpaar gliedert also eine logische Befehlszeile. Wenn Sie das folgende Kommando eingeben, wird zuerst der Prozess MakeCSV.exe gestartet und dann bei Beendigung der Errorlevel überprüft. Ist dieser ungleich 0, dann erfolgt die Ausgabe mit echo, ansonsten wird csvde aufgerufen. ( MakeCSV.exe datei.ext || echo Probleme! ) && csvde -i -f datei.ext
90
Befehlsgruppierung
Natürlich können Sie mehr als eine Klammernebene schachteln. Die Grenze ist auch hier die maximale Länge der Eingabezeile bzw. das eigene Verständnis. Wenn Sie sehr komplexe Kommandos schreiben, werden Sie schnell merken, dass dadurch nicht wirklich Arbeit gespart wird. Es existiert mit den Makros aber eine Funktion, diese langen Kommandos einfach einem kurzen Makronamen zuzuweisen. Dann haben Sie die Vorteile mächtiger Befehle und einfacher Eingabe kombiniert. Mehr dazu im Kapitel 10. Diese Gruppierung gilt auch für die Umleitung von Ein- und Ausgabe. Die folgenden beiden Kommandos sind nicht funktionsgleich: 01: echo Zeile 1 && echo Zeile 2 & echo Zeile 3 > x.x 02: ( echo Zeile 1 && echo Zeile 2 & echo Zeile 3 ) > x.x
Während in der Zeile 1 nur der Text „Zeile 3“ in die Datei geschrieben wird, enthält die Datei nach der Ausführung von Zeile 2 alle drei Zeilen! Es gibt aber noch einen weiteren Einsatzzweck für die Gruppierung von Kommandos. Dieser ist notwendig, da manche Befehle der Shell ihren Ursprung weit in der DOS-Zeit haben. Die Funktionalität dieser Befehle kann aus Kompatibilitätsgründen nicht einfach mit den Befehlserweiterungen neuerer Windows-Versionen verändert werden. Daher muss unter Umständen eine Gruppierung mit den Klammern erfolgen. Sehen wir uns dies an einem Beispiel an. Welche Ausgabe produziert das folgende Kommando, wenn die aktuelle Uhrzeit 16:30 lautet? if %time:~0,2% GTR 12 echo Nachmittag else echo Vormittag
Sollen Sie jetzt vermutet haben, dass die Ausgabe Nachmittag lautet, geht es Ihnen wahrscheinlich wie den meisten Lesern. Dies wäre sinnvoll, widerspricht aber der seit DOS definierten Funktionalität des Kommandos echo. Dieses gibt den gesamten Text bis zum Zeilenende aus. Die Ausgabe lautet daher: Nachmittag else echo Vormittag
Für die korrekte Funktion muss echo in einer eigenen logischen Kommandozeile stehen. Dies kann durch eine Gruppierung mit runden Klammern erreicht werden. if %time:~0,2% GTR 12 (echo Nachmittag) else (echo Vormittag)
Wird die Kommandozeile so umgebaut, dann erhalten Sie auch die korrekte Ausgabe "Nachmittag" (zumindest war es, als ich diese Zeilen geschrieben habe, Nachmittag).
91
8 Befehlsverknüpfung
92
9
Ablaufsteuerung
Sie werden sich vielleicht wundern, wo bei interaktiven Kommandos eine Ablaufsteuerung Sinn macht. Aber nicht nur in Shell Scripts, sondern bereits bei komplexeren Kommandos oder verketteten Befehlen (die sich dann auch in Makros gießen lassen, wie Sie in Kapitel 10 erfahren) kann eine bedingte oder mehrfache Kommandoausführung Sinn machen. Den Kontrollstrukturen in Batches wird ein eigenes Kapitel (11.4) gewidmet.
9.1
Bedingte Befehlsauführung
9.1.1
Grundlagen von if
Auch bei Shell Scripts besteht die Notwendigkeit, die Abarbeitung von Kommandos von bestimmten Bedingungen abhängig zu machen. Ein Konstrukt für die Verzweigung des Ablaufs existiert in nahezu jeder Programmiersprache, allerdings in unterschiedlicher Form. Das Kommando if verfügt über eine Reihe von Optionen, die aber alle dem gleichen Muster folgen: if [NOT] [else ]
Durch die Angabe von NOT vor dem Test lässt sich jede Testbedingung in ihrer Wirkung umdrehen, d.h., ein Kommando wird dann beispielsweise ausgeführt, wenn eine Datei nicht vorhanden ist. Soll der else-Teil nicht behandelt werden, kann dieser Teil des Befehls einfach weggelassen werden. Dabei muss sowohl das Kommando1 als auch das Kommando2 in einer eigenen logischen Befehlszeile stehen. Wie bereits bei der Verkettung von Befehlen besprochen, müssen daher einige Befehle wie echo, del u.a. in Klammern gruppiert werden. Dabei darf natürlich auch ein Zeilenumbruch enthalten sein. Das folgende Beispiel ist ein gültiges Kommando und kann so beispielsweise in einem Shell Script verwendet werden. IF EXIST test.txt ( del test.txt ) ELSE ( echo test.txt fehlt )
93
9 Ablaufsteuerung
9.1.2
Formen von if
Dateiprüfung Mit dieser Form des if-Kommandos kann die Existenz von Dateien und Verzeichnissen überprüft werden. Da wird nach dem Schlüsselwort exist eine Datei- oder Pfadmaske angegeben. Ist eine dieser Maske entsprechende Datei vorhanden, wird das nachfolgende Kommando ausgeführt, andernfalls nicht. if exist c:\imp\eingabe.dat Machwas.exe eingabe.dat
Dieses Kommando führt das Programm machwas.exe nur dann aus, wenn die Datei eingabe.dat vorhanden ist. Der Test auf ein Verzeichnis ist relativ einfach, da es eine Reihe von reservierten Dateinamen gibt, die seit DOS für bestimmte logische Geräte stehen. Eines davon ist das so genannte nul device mit Namen nul. Diese Datei wird bei einem Test mit exist immer als vorhanden erkannt. Wenn ich feststellen möchte, ob das Verzeichnis e:\daten auf meinem Computer existiert, kann ich dies ganz einfach tun, indem ich in diesem Verzeichnis die Datei mit dem Namen nul suche: if exist e:\daten\nul echo Verzeichnis e:\daten ist vorhanden!
Eine andere Möglichkeit, auf das Vorhandensein eines Verzeichnisses zu testen, ist der Test auf den Dateinamen „.“, der für das aktuelle Verzeichnis steht. Da ein Verzeichniseintrag „.“ nur dann vorhanden ist, wenn das Verzeichnis existiert, ist dies als Testbedingung ebenso möglich. So können Sie in Ihren Batches Verzeichnisse sicher anlegen. Fragen Sie vorher einfach ab, ob das Verzeichnis noch nicht existiert. if not exist c:\tmp\test\. md c:\tmp\test
Test auf Variablen Die zweite Möglichkeit des if-Kommandos eignet sich dazu, das Vorhandensein einer Umgebungsvariablen abzufragen. Getestet wird hier nicht ein bestimmter Inhalt, sondern nur, ob die Variable überhaupt definiert ist. So wird beispielsweise automatisch die Variable userprofile definiert, in der der Pfad zum Profil des angemeldeten Benutzers gespeichert wird. Soll nun ein Kommando alle Verknüpfungen auf dem Desktop auflisten, muss dazu diese Variable auch definiert sein. Ansonsten würde ein Kommando in der Form dir "%userprofile%\ desktop\*.lnk" fehlschlagen. Mit einem if defined userprofile vor dem dir-Kommando kann eine entsprechende Abfrage erfolgen. Der Name der gesuchten Variable wird dabei ohne umschließende Prozentzeichen angegeben (die Anführungszeichen sind notwendig, da der Pfad ja auch Leerzeichen enthalten könnte): if defined userprofile (dir "%userprofile%\desktop\ *.lnk") else (echo Kein Profilpfad definiert!)
94
Bedingte Befehlsauführung
Auch hier sind die Klammern notwendig. Ohne die Klammern wird bei einem erfolgreichen Test das Kommando dir ausgeführt. Dieses ist aber so implementiert, dass alles bis zum Ende der logischen Zeile als Parameter und Dateisuchmaske aufgefasst wird. Daher muss das dirKommando in die runden Klammern eingeschlossen und die logische Zeile so begrenzt werden. Ebenso gibt echo alles bis zum Zeilenende als Text aus. Falls Sie nun einwenden, dass an dieser Stelle die Klammern dann überflüssig sind, haben Sie Recht. Dennoch sollten Sie sich diese Schreibweise angewöhnen. Wird später einmal die Reihenfolge der Aktionen gedreht oder ein Befehl durch Kopieren an eine andere Stelle dupliziert, könnte ohne die Klammern ein Fehler auftreten. Errorlevel testen Für den Test des Rückgabecodes eines Prozesses (der errorlevel) existiert eine weitere Variante von IF. Dies ist auch die einzige Form von IF, bei der es bei mehrfachen Tests auf die Reihenfolge der Befehle ankommt. Die allgemeine Form lautet hier: if errorlevel <wert>
Beim ursprünglichen Design der Shell (noch zu guten, alten DOS-Zeiten) ging man davon aus, dass Fehlercodes hierarchisch gestaffelt werden, also ein Errorlevel 4 einen gravierenderen Fehler als ein Errorlevel von 1 darstellt. Deshalb erfolgt der Vergleich bei if errorlevel immer mit einem Operator „größer als“. Wurde ein Prozess beendet und liefert einen Rückgabecode von 7, so wird auch der Vergleich if errorlevel 2 ein Ergebnis von logisch wahr liefern, und das Kommando würde ausgeführt. Innerhalb einer Batchdatei sollte also immer zuerst der größtmögliche Errorlevel abgefragt werden und zum Ende hin der kleinste Wert. Ihr Batchfile sollte beim Abfragen mehrerer Fehlercodes also ungefähr so aussehen: if errorlevel 8 echo Unbekannter Befehl if errorlevel 4 echo Ungültiges Verzeichnis if errorlevel 2 echo Datei nicht gefunden if errorlevel 1 echo Prozess abgebrochen echo Erfolgreich beendet
Falls Sie diesen Test auf „größer oder gleich“ nicht benutzen wollen, können Sie auch die Variable ERRORLEVEL direkt mit einem allgemeinen Vergleich testen. Diesen Ansatz stelle ich Ihnen jetzt zum Abschluss von if vor. Allgemeine Vergleiche Am flexibelsten ist if bei allgemeinen Vergleichen zweier Zeichenketten oder zweiter nummerischer Werte. Die allgemeine Syntax hierfür lautet: if [/I]
Als Operator kann einer der Operatoren aus der folgenden Tabelle verwendet werden.
95
9 Ablaufsteuerung
EQU (oder ==)
gleich
NEQ
nicht gleich
LSS
kleiner als
LEQ
kleiner als oder gleich
GTR
größer als
GEQ
größer als oder gleich
Diese etwas merkwürdige Gestaltung der Operatoren hängt damit zusammen, dass die mathematische Notation (z.B. >=) nicht möglich ist. Die eckigen Klammern werden ja in der Shell für die Umleitung von Ein- oder Ausgabekanal benötigt, daher werden die Operatoren hier in Textform verwendet. Anstelle des Operators equ kann auch das seit DOS implementierte doppelte Gleichheitszeichen verwendet werden. Auch hier sind alle Kommandos in einer Zeile ohne Umbruch einzugeben. Einige Beispiele für den Einsatz von IF: if/i
%cd% == %systemroot% goto :eof
Falls das aktuelle Verzeichnis das Systemverzeichnis von Windows ist, wird das Shell Script beendet (gute Notbremse für Löschaktionen und andere Batches). if defined lang if /i %lang% == DE (echo Willkommen) else if /i %lang% == IT (echo Benvenuto) else (echo Welcome)
Ausgabe von Meldungen je nach eingestellter Sprache (nicht optimale Lösung): if /i %username% == Administrator ...
Nur, wenn der Benutzername Administrator lautet … if %errorlevel% == 17 echo ExitCode 17
Wird nur ausgegeben, wenn der Errorlevel genau 17 ist, bei allen anderen Werten erfolgt keine Ausgabe. Das letzte Beispiel zeigt nochmals eine Abfrage von Werten für den Errorlevel, allerdings mit einem direkten Vergleich. Mit diesem Ansatz können Sie die Werte im Gegensatz zur Form IF ERRORLEVEL in beliebiger Reihenfolge abfragen.
9.2
Das mächtige for
Wie in jeder Shell und jeder Programmiersprache existiert auch in der Shell von Windows die Möglichkeit, Anweisungen mehrfach auszuführen. Prinzipiell existieren in der Windows Shell zwei Möglichkei-
96
Das mächtige for
ten, die beide ihre Berechtigung besitzen. Die eine ist die Erzeugung einer Schleife durch eine Testbedingung verbunden mit einem goto in einer Batchdatei, die andere Version nutzt die Möglichkeiten des Kommandos for. Diese Möglichkeit eignet sich auch gut für interaktive Kommandos und soll im Folgenden besprochen werden.
9.2.1
Grundlagen
Viele Programmiersprachen besitzen ein sprachliches „Schwergewicht“ in ihrem Vokabular, mit sich immer wieder neue Möglichkeiten erschließen und an dem man als Einsteiger lange hängen bleibt. Bei den Shell Scripts ist dies mit Sicherheit das Kommando for. Von einfachen Zählschleifen bis hin zu komplexen Ersetzungen und sogar dem von UnixLiebhabern immer wieder geforderten Backquoting (s. Abschnitt 9.2.8) ist alles möglich24. Aber fangen wir mit den einfachen Dingen an … Die allgemeine Syntax für das Kommando for lautet wie folgt: for [] in (<list>) do
Die Variable erhält nacheinander jeden Wert aus <list>, und für jede Ersetzung wird einmal das ausgeführt. Die Variable kann ein Buchstabe von a bis z sein, der mit einem vorangestellten Prozentzeichen gekennzeichnet wird. In Batchfiles muss dieses Prozentzeichen verdoppelt werden! Als Kommando kann jedes Kommando verwendet werden, das auch in einer einzelnen Kommandozeile gültig ist. Über den Namen der Variablen kann der jeweilige Ersetzungswert (auch mehrfach) dem Kommando als Parameter übergeben werden. Das Kommando kann auch selbst wieder eine for-Schleife sein, eine Schachtelung muss nur verschiedene Namen für die Variablen verwenden.
9.2.2
Einfache Schleifen mit Werte- und Dateilisten
Das Kommando for %f in ( eins zwei drei ) do echo %f
ergibt folgende Ausgabe: echo eins eins echo zwei zwei echo drei drei
24 iner der Testleser des Manuskripts hat es so formuliert: „for ist in Wirklichkeit die Abkürzung für ‚fast ohne Restriktionen’“.
97
9 Ablaufsteuerung
Wie Sie erkennen, wird das Kommando (hier echo) dreimal ausgeführt. Wollen Sie beispielsweise die Wertigkeit der einzelnen Bits in einem Byte ausgeben, reicht folgende Zeile: for %f in ( 0 1 2 3 4 5 6 7 ) do set /A "1 << %f"
Die Ausgabe dazu sieht dann so aus: C:\tmp>set 1 C:\tmp>set 2 C:\tmp>set 4 C:\tmp>set 8 C:\tmp>set 16 C:\tmp>set 32 C:\tmp>set 64 C:\tmp>set 128
An dieser Ausgabe stört Sie wahrscheinlich das Gleiche wie mich: Das Kommando der Schleife wird jedes Mal mit ausgegeben. Besser wäre es, wenn wirklich nur die Ausgabe erscheinen würde. Dieses kosmetische Problem lässt sich leicht lösen. Die Ausgabe des Kommandos wird durch ein vorangestelltes „@“ unterdrückt. Durch die Änderung des Kommandos in for %f in ( eins zwei drei ) do @echo %f
ändert sich die Ausgabe zu: eins zwei drei
Diese Schleifen funktionieren auch mit der Angabe von Dateinamen als Wertelemente der Liste. Enthält ein Element der Liste eines der beiden Zeichen „*“ oder „?“, so wird dieses Element als Suchmaske für eine Datei aufgefasst, und alle auf diese Suchmaske passenden Dateinamen werden zurückgeliefert. Das Kommando for %f in (c:\tmp\ *.jpg) do @echo %f liefert somit eine Liste aller Dateien mit der Erweiterung .jpg im Verzeichnis c:\tmp, funktional also ein etwas umständlicher Ersatz für ein dir c:\tmp\*.jpg. Dennoch kann das for-Kommando wesentlich mehr, sobald es über das simple Auflisten von Dateinamen hinausgeht. Dazu ein Beispiel aus der Praxis: Als Hobbyfotograf habe ich in meiner Werkzeugsammlung ein kleines Freeware-Tool, das per Kommandozeile den Kommentar in einer JPGDatei anzeigt. Allerdings akzeptiert das Tool keine Wildcards, sondern
98
Das mächtige for
immer nur einen Dateinamen. Trotzdem kann ich eine Liste der Bildkommentare für ein ganzes Verzeichnis erzeugen: for %f in (.jpg) do @jpginfo %f /comment >> jpg.lst
Hier wird in der Schleife nacheinander jede gefundene JPG-Datei in die Variable %f eingesetzt und dann das Kommando jpginfo ausgeführt. Die Ausgaben werden durch eine Umleitung alle an die Datei jpg.lst angehängt, und schon habe ich meine Liste mit den Bildkommentaren. Geschachtelte Schleifen sind ebenfalls einfach zu realisieren. Vor allem für das Erzeugen von Testdaten ist nicht viel Aufwand nötig. Das folgende Beispiel (gesamte Eingabe in einer Zeile) erzeugt eine Liste aller möglicher Kombinationen der beiden Listen (in SQL ein Cross Join) und gibt das Ergebnis aus: for %f in (Karl Gabi Franz Evi) do @for %g in (Joggen Schwimmen Surfen) do @if not %f == %g echo %f %g
Die Ausgabe sieht dann wie folgt aus: Karl Joggen Karl Schwimmen Karl Surfen Gabi Joggen Gabi Schwimmen Gabi Surfen Franz Joggen Franz Schwimmen Franz Surfen Evi Joggen Evi Schwimmen Evi Surfen
Weiter unten erfahren Sie dann auch noch, wie die Listen statt einer direkten Eingabe auch aus bestehenden Dateien gelesen werden können.
9.2.3
Variablenersetzung bei Schleifen
Im vorigen Abschnitt habe ich nur kurz erläutert, dass die Variable ein Buchstabe (a–z) sein darf. Zur Kennzeichnung als Variable wird diesem Buchstaben ein Prozentzeichen vorangestellt. Soll eine Schleife mit for in einem Shell Script verwendet werden, muss dieses Prozentzeichen verdoppelt werden. Die Batch-Engine der Shell erkennt an den Prozentzeichen nämlich auch die übergebenen Parameter (%1 bis %9), daher würde bei einem einzelnen Prozentzeichen für eine Schleifenvariable ein Laufzeitfehler erzeugt. Überall, wo diese Variable im Kommando der Schleife eingesetzt wird, wird diese zur Laufzeit durch das jeweils aktuelle Element aus der Liste ersetzt. Falls es sich bei den Elementen um Dateinamen handelt, müssen Sie
99
9 Ablaufsteuerung
unter Umständen die Variable in doppelte Anführungszeichen einschließen, da sonst Leerzeichen im Namen nicht erkannt werden. for %f in (*.txt) do machwas.exe "%f"
Ohne die oben angegebenen Anführungszeichen würde der Dateiname nicht als ein Parameter übergeben, wenn im Dateinamen Leerzeichen enthalten sind. Zusätzlich erlaubt die Shell noch eine weitergehende Ersetzung der Variablen durch ihren Inhalt. Sind Sie nicht am kompletten Dateinamen interessiert, sondern benötigen Sie nur die Erweiterung oder nur den Laufwerksbuchstaben, müssen Sie nur zwei Zeichen ändern. Für die Auflistung der einzelnen Optionen gehe ich von folgendem Kommando aus: for %b in ("test 12.htm") do @echo
Die folgende Tabelle zeigt die möglichen Variationen, die Sie anstelle von einsetzen können, und die resultierende Ausgabe. Tabelle 9.1: Liste der möglichen Variablenersetzungen bei FOR
%b
Listenelement
test 12.htm
%~b
Entfernt evtl. Anführungszeichen
test 12.htm
%~fb
Vollständiger Dateiname
c:\tmp\test 12.htm
%~db
Laufwerksbuchstabe
C:
%~pb
Pfad
\tmp\
%~nb
Name der Datei
test 12
%~xb
Erweiterung
.htm
%~sb
8.3 Name
c:\tmp\TEST12~1.HTM
%~ab
Attributliste
--a------
%~tb
Zeitangabe der Datei
26.02.04 22:33
%~zb
Größe der Datei
280
Ein Sonderfall stellt die Ersetzung mit %~$PATH:b dar. Hier wird der Suchpfad für Befehle (die in der Variablen PATH enthaltene Liste von Verzeichnissen) durchsucht und die erste gefundene Datei eingesetzt. Diese etwas merkwürdig anmutende Kommandofolge wird von vielen Administratoren oft gesucht, die nach einem Äquivalent für which suchen. Das gibt es, das Kommando dafür sieht so aus (entspr. which notepad.exe): for %f in (notepad.exe) do echo %~$PATH:f
Die Ausgabe bringt das erste Verzeichnis aus dem Suchpfad, in dem die gesuchte Datei vorhanden ist: C:\WINNT\system32\notepad.exe
Im Kapitel „Makros“ erfahren Sie, wie sich dieses Kommando dann auch als Kommando which in die Windows Shell integrieren lässt.
100
Das mächtige for
Das folgende Beispiel zeigt nicht nur die Mächtigkeit des for-Kommandos, sondern ist auch ein schönes Beispiel dafür, dass Sie das optimale Werkzeug für die jeweilige Aufgabe einsetzen sollten. Viele Admins haben sich in den Windows Script Host eingearbeitet und versuchen, möglichst jede Aufgabe mit einem kleinen vbs-Script zu lösen, da „die Shell von Windows ja nur ein besserer command.com ist“. Nehmen Sie an, in einem Verzeichnis liegen Dateien eines Satzsystems (z.B. LaTeX), die in das PDF-Format konvertiert werden. Sie benötigen nun eine Liste all jener Datei, für die diese Konvertierung noch nicht stattgefunden hat. Manuell ist dies bei mehr als 50 Dateien kaum mehr in vernünftiger Zeit machbar. Der nächste Ansatz hieße daher: WSH. Damit Sie die Kommandos nachvollziehen können, werde ich das Szenario etwas abwandeln: Gesucht sind alle exeDateien im system32-Ordner, für die eine gleichnamige DLL existiert. Hier das VB-Skript dazu: const cPATH = "c:\winnt\system32\" set fso = CreateObject("Scripting.FilesystemObject")
Listing 9.1: VB-Skript zur Suche nach Dateien
set folder = fso.GetFolder(cPATH) for each file in folder.Files if LCase(Right(file.Name, 4)) = ".exe" then s = fso.GetBaseName(file.Name) + ".dll" if not fso.FileExists(cPATH & s) then WScript.Echo "FILE: " & file.name end if end if next set fso = Nothing set folder = Nothing
Mit Hilfe einer Filesystem-Object-Instanz wird die Liste aller Dateien im gesuchten Ordner durchlaufen. Handelt es sich dabei um eine Datei mit der Erweiterung .exe, wird die Erweiterung in .dll umgebaut. Dann wird mit Hilfe der Methode FileExists getestet, ob eine solche Datei im Ordner vorhanden ist. Wenn ja, erfolgt die Ausgabe des Dateinamens. Abgesehen von der Länge des Skripts müssen Sie für diesen Ansatz programmieren können. Sie sollten eine Ahnung haben, wie COMObjekte benutzt werden und welches Objekt welche Methoden bereithält. Kein Problem für Entwickler und programmierende Administratoren, aber alle anderen haben bei diesem Ansatz Pech gehabt (es sei denn, Sie tippen das Skript irgendwo ab). Wesentlich einfacher ist da die Nutzung der Möglichkeiten der Shell. Ganz ohne Programmierung und mit einer logisch leicht nachzuvollziehenden Befehlszeile kommen wir hier mit einem Einzeiler aus
101
9 Ablaufsteuerung
(wer keine Lust hat, zweimal den Pfad c:\winnt\system32\ zu tippen, wechselt mit cd vorher in dieses Verzeichnis und lässt den Pfad im Kommando einfach weg): for %f in (c:\winnt\system32\*.exe) do @if exist c:\ winnt\system32\%~nf.dll echo %f
Mit dieser Schleife werden alle exe-Dateien durchlaufen, und für jede Datei wird mit dem if exist-Kommando getestet, ob es eine Datei gibt, die den gleichen Namen (%~nf) besitzt, nur mit .dll dahinter. Falls ja, wird der Name der exe-Datei ausgegeben (echo %f). So wie der WSH hat also auch die Shell bei Windows 2003 durchaus ihre Berechtigung als Mittel zur Aufwandsminimierung.
9.2.4
Schleifen für Verzeichnisse und Unterverzeichnisse
Die Suche nach Dateien kann durch die Option /D auf Verzeichnisse statt Dateien angewendet werden. Zusätzlich existiert die Option /R , mit der eine for-Schleife auf alle Verzeichnisse unterhalb des angegebenen Pfades angewendet wird. Angenommen, Sie möchten wissen, welche Verzeichnisse für Bilder (die üblicherweise den Namen images tragen) in Ihren selbst erstellten Websites vorhanden sind. Wenn diese unter dem Verzeichnis d:\daten\websites liegen, hilft folgendes Kommando weiter: FOR /R d:\daten\websites %v IN (images) DO @if exist %v\. echo %v
Die Ausgabe listet dann alle Verzeichnisse auf, in denen das Verzeichnis images existiert, wie das folgende Beispiel zeigt: d:\daten\websites\demo01\images d:\daten\websites\demo01\fotos\images d:\daten\websites\demo01\hobbies\images d:\daten\websites\firmaXY\images d:\daten\websites\firmaXY\produkte\images
Die allgemeine Form dieser Schleife lautet: FOR /R %v IN (<maske>) do
Damit bieten sich auch für „Aufräumarbeiten“ genügend Einsatzmöglichkeiten, da die erweiterte Variablenersetzung auch hier funktioniert. Ein immer wieder von Administratoren beklagtes Problem sind riesige pst-Dateien von Microsoft Outlook-Benutzern, die damit ihr privates Archiv aller Mails der letzten fünf Jahre schaffen. Mit Hilfe von for und /R ist das Durchsuchen einer Platte nach solchen Ungetümen kein Problem mehr. Damit Sie dies auf Ihrem Rechner nachvollziehen können, baue ich die Aufgabenstellung etwas um. Welche zip-Dateien mit mehr als 5 MByte liegen auf dem Laufwerk C: Ihres Computers? FOR /R c:\ %v IN (*.zip) DO @if %~zv GTR 5242880 echo %v
102
Das mächtige for
Dieses Kommando funktioniert folgendermaßen: Mit der forSchleife wird ausgehend von c:\ nach allen zip-Dateien gesucht. Die erste if-Abfrage stellt fest, ob eine solche Datei überhaupt existiert. Falls ja, testet die zweite if-Abfrage mit Hilfe der erweiterten Variablenersetzung, ob die Größe der Datei 5 MByte übersteigt. Falls ja, wird der Name der Datei inklusive Pfad ausgegeben. Ich wünsche Ihnen viel Spaß auf der Suche nach den E-Mail-Archivaren in Ihrem Unternehmen.
9.2.5
Zählschleifen
Oft benötigen Sie eine bestimmte Anzahl von Befehlsausführungen. So muss für ein Benchmark ein Prozess genau fünfzehn Mal gestartet werden, eine Datei benötigt genau 20 Zeilen, oder Sie wollen einen Ping mit steigenden Paketgrößen absetzen. Für diese Zwecke existiert die Option /L (für loop) des for-Kommandos. Hier sind die drei Kommandos für diese Aufgabenstellungen: for /L %a in (1,1,15) do start notepad.exe for /L %f in (1,1,20) do echo Zeile %f >> datei.txt for /L %f in (64,64,512) do @ping kiste -n 1 -l %f
Wie Sie aus den Beispielen erkennen, nimmt die Liste hier die Grenzen und Schrittweiten der Schleife auf. Der erste Parameter gibt den Startwert an, danach kommt die Schrittweite, als dritter Parameter der Endwert für die Schleife. Im Gegensatz zu früheren Windows-Versionen, wo dies manuell mit geschachtelten Schleifen für Einer- und Zehnerstellen nur ungenügend simuliert werden konnte, stellt diese Option eine deutliche Erleichterung dar.
9.2.6
Lesen aus Dateien
Nicht immer kommen die Daten für eine Schleife direkt aus der Kommandozeile. In vielen Fällen liegen die Daten bereits in Form einer Textdatei vor, die bearbeitet werden soll. Auch hier existiert eine Variation der for-Schleife, mit der aus Dateien gelesen werden kann. Für die folgenden Beispiele habe ich eine kleine Testdatei input.txt erstellt, die Sie mit den Beispielen im Download-Archiv zum Buch finden: # das ist ein Kommentar Franz Testhuber;[email protected];DE Karl Klammer;[email protected];UK Gabi Jedermann;[email protected];DE Evi Eilig;[email protected];IT
Listing 9.2: Inhalt der Beispieldatei input.txt
103
9 Ablaufsteuerung
Für das Lesen aus einer Datei wird bei for die Option /F (für file) verwendet, die durch einige weitere Parameter gesteuert werden kann. Hier die einfachste Form zum Lesen aus einer Datei: for /F %i in (input.txt) do @echo %i
Wie Sie erkennen, wird als Listenelement der Name der zu lesenden Datei eingesetzt, und vor der Schleifenvariablen (hier %i) wird der Optionsschalter /F angegeben. Die Ausgabe allerdings ist möglicherweise nicht das, was Sie erwartet haben: Listing 9.3: Die Ausgabe der for-Schleife
# Franz Karl Gabi Evi
Ohne besondere Parameter geht for davon aus, dass Sie nur am ersten Wort jeder Zeile interessiert sind, wobei wie bei natürlichen Sprachen als Worttrenner das Leerzeichen und der Tabulator fungieren. Die zur Steuerung dieses Verhaltens notwendigen Parameter finden Sie in der nachfolgenden Tabelle. Tabelle 9.2: Mögliche Parameter für die Option /F bei for-Schleifen
Parameter
Bedeutung
Beispiel
eol=c
Legt als Zeilentrennzeichen das Zeichen c fest (nur eines erlaubt).
eol=#
skip=n
Überspringt n Zeilen am Anfang der Datei.
skip=1
delims=xxx
Legt die Trennzeichen zwischen einzelnen Spalten fest (mehrere Zeichen möglich).
delims=;,:
tokens=x,y,m-n
Legt fest, welche Spalten gelesen werden sollen, ein Stern als letztes Zeichen enthält den Rest der Zeile.
tokens=1,*
usebackq
Aktiviert backquoting mit neuer Syntax (` als Zeichen).
siehe unten
Ich bin mir sicher, dass Sie für die beiden Parameter eol und skip nicht auf Beispiele angewiesen sind. Die restlichen Parameter möchte ich gerne anhand von Beispielen vorstellen, und für usebackq verweise ich Sie auf den Abschnitt mit dem Titel „Backquoting“ weiter unten. Fast immer werden Sie in den for-Schleifen den Parameter tokens in Kombination mit delims verwenden, da diese erst festlegen, welche Spalte(n) gelesen wird bzw. werden. Um die komplette Zeile aus unserer Beispieldatei zu lesen, muss das Kommando wie folgt abgewandelt werden: for /F "tokens=*" %i in (input.txt) do @echo %i
104
Das mächtige for
Wie leicht zu erkennen ist, werden alle zu /F gehörenden Parameter direkt dahinter in doppelten Anführungszeichen eingeschlossen. Nun sieht die Ausgabe so aus wie erwartet: # das ist ein Kommentar Franz Testhuber;[email protected];DE Karl Klammer;[email protected];UK Gabi Jedermann;[email protected];DE Evi Eilig;[email protected];IT
Ein Stern als Wortnummer bei tokens expandiert die Variable also zum kompletten Rest der Zeile. Da vorher keine Zahl steht, ist der Rest hier gleichbedeutend mit der ganzen Zeile. Sind Sie nur an der E-MailAdresse interessiert, ist dies bei einem Semikolon als Trennzeichen das zweite Wort der Zeile. Das entsprechende Kommando lautet also: for /F "tokens=2 delims=;" %i in (input.txt) do @echo %i
Obwohl ich kein skip=1 angegeben hatte, erscheint die erste Zeile mit dem Kommentar nicht in der Ausgabe! Wieso eigentlich? Da in der ersten Zeile kein Semikolon vorkommt, zählt diese komplette Zeile als erstes Wort (vor dem ersten Trennzeichen). Wäre in der ersten Zeile ein Semikolon enthalten, würde auch der Inhalt dieser Zeile in die Ausgabe gelangen. Denken Sie also daran, etwaige Kopfzeilen wie in unserer Beispieldatei immer mit skip zu überspringen. Sollten Sie nicht wissen, wie viele Kopfzeilen in der Datei enthalten sind, oder sich mitten in der Datei noch Kommentare befinden, müssen Sie jede Zeile erst auf das vereinbarte Kommentarzeichen testen. Da sich dies nicht mehr so einfach in einem einzelnen Kommando hinter der Schleife erledigen lässt, ist dafür der Aufruf einer Subroutine nötig. Wie Sie diese realisieren, finden Sie in den Kapiteln über Selbstaufruf und Modularisierung bei Shell Scripts im zweiten Teil des Buches. Benötigen Sie mehr als eine Spalte, kann tokens auch mit mehr als einer Zahl versorgt werden. Das folgende Kommando liest den Namen und die Landeskennung aus der Datei aus: for /F "skip=1 tokens=1,3 delims=;" %i in (input.txt) do @echo %i ist aus %j
Wie Sie diese Kommandozeile eingeben, erhalten Sie die unten abgedruckte Ausgabe: Franz Testhuber ist aus DE Karl Klammer ist aus UK Gabi Jedermann ist aus DE Evi Eilig ist aus IT
105
9 Ablaufsteuerung
Wenn Sie das Kommando noch einmal genauer betrachten, sehen Sie, dass ich für die Schleifenvariable %i gewählt habe. Dennoch taucht am Ende des Kommandos auch ein %j auf. Dies liegt daran, wie Microsoft die Zuweisung der einzelnen Spalten an die Schleifenvariable implementiert hat. Da wir bei tokens mehr als einen Wert angeben, geht der Inhalt des ersten Wertes (1 für das erste Wort) in die Variable %i. Als nächsten Wert habe ich 3 angegeben, um das dritte Wort (die Landeskennung) zu erhalten. Nun, der nächste Wert geht in die nächste Variable, und diese ist %j, weil j auf i folgt. Hätte ich die Schleife mit %a geschrieben, wäre für das nächste Wort in der tokens-Liste eben %b nötig gewesen. Nicht unbedingt auf den ersten Blick einsichtig, aber es funktioniert.
9.2.7
Parsing von Zeilen
Auch wenn Sie keine Daten aus einer Datei lesen, sind Sie in der Lage, einzelne, direkt eingegebene Zeilen mit den obigen Möglichkeiten zu parsen. Dazu wird ebenfalls die Option /F angegeben, aber die Zeile als Listenelement in die Klammern geschrieben, umschlossen von doppelten Anführungszeichen. for /F "delims=; tokens=3" %a in ("Ardbeg;17 Jahre;46%; Eichenfass") do @echo %a
Durch eine Kombination bzw. Schachtelung von for-Schleifen lassen sich so auch weitergehende Zerlegungen durchführen, wie das Beispiel unten zeigt, das Sie am besten erst einmal in seine Einzelteile zerlegen. for /F "tokens=1,2 skip=1 delims=;" %i in (input.txt) do @for /F "delims=@ tokens=2" %a in ("%j") do @echo Domain für %i = %a
Bevor ich dieses Kommando im Einzelnen erläutere, hier noch die Ausgabe des Kommandos, damit Sie die einzelnen Schritte mitverfolgen können: Domain Domain Domain Domain
für für für für
Franz Testhuber = firma.de Karl Klammer = nervig.com Gabi Jedermann = firma.de Evi Eilig = somewhere.org
Was passiert genau? 왘 Zuerst wird per Schleife aus der Datei input.txt jede Zeile außer der ersten gelesen. Als Trennzeichen wird das Semikolon definiert, und es soll das erste und das zweite Wort gelesen werden 왘 Das erste und zweite Wort jeder Zeile stehen in den beiden Variablen %i und %j zur Verfügung. 왘 Als Kommando dieser Schleife wird eine neue for-Schleife erzeugt, die als Listenelement für ein direktes Zeilenparsing den Inhalt von %j erhält. Damit dies funktioniert, muss ebenfalls /F angegeben werden, und die als Listenelement verwendete Zeile wird in doppelte Anführungszeichen eingeschlossen.
106
Das mächtige for 왘 Als Trennzeichen für die zweite Schleife ist aber das Zeichen @
definiert (Trennung zwischen user und domain), und es soll das zweite Wort dieses Ausdrucks verwendet werden (der Domänenanteil). 왘 Im Kommando für die zweite Schleife wird nun noch per echo die
Ausgabe erzeugt, in dem mit %i der Name aus der ersten Schleife und mit %a der Domänenteil der zweiten Schleife ausgegeben wird. Achten Sie bei solchen geschachtelten Schleifen nach Möglichkeit immer darauf, dass keine Konflikte bei den Variablennamen auftreten. Solche Fehler sind nur mit hohem Zeitaufwand zu finden.
9.2.8
Backquoting
Backquoting ist eine Funktionalität der Windows Shell, auf die ich nur etwa 15 Jahre lang gewartet habe, seit ich sie bei Unix schätzen gelernt habe. Da Sie als Leser aber eher aus der Windows-Welt kommen, hier eine kurze Beschreibung des Konzepts: Wenn Sie in einer Kommandozeile einen Teil in Backquotes (einzelne, rückwärts gewandte Anführungszeichen, „`“, Unicode Ux0060) einschließen, wird dieser Teil als Befehl aufgefasst, und die Ausgabe dieses Befehls ersetzt den Teil der Kommandozeile in Backquotes. In Unix: echo Sie sind `whoami` – und damit ist es sehr einfach. In der Windows Shell allerdings hat Microsoft diese Funktionalität so gut und kompliziert im for-Kommando versteckt, dass man verzweifeln möchte. Trotzdem ist diese Funktionalität, vor allem zusammen mit Makros, sehr gut einsetzbar. Als Beispiel soll noch einmal eine Aufgabenstellung vom Beginn des Buches gelöst werden: die Frage, welches Programm für eine bestimmte Erweiterung zuständig ist. Dazu sind zwei Kommandos nötig: assoc und ftype. Mit assoc wird zuerst festgestellt, wie der Dateityp für eine bestimmte Erweiterung heißt, danach wird mit ftype der Handler für diesen Dateityp ausgelesen. H:\Shell-Buch\samples>assoc .txt .txt=txtfile H:\Shell-Buch\samples>ftype txtfile txtfile=C:\WINNT\system32\NOTEPAD.EXE %1
Wie die Ausgabe von assoc zeigt, wird für ftype eigentlich nur das zweite Wort einer durch Gleichheitszeichen getrennten Zeile benötigt. Verpackt man den Aufruf von assoc als Listenelement in eine for-Schleife und umgibt ihn mit Backquotes, muss bei den Parametern für /F nur noch usebackq mit aufgenommen werden. Hier ist die Erklärung viel länger als das Kommando, aus dem die Funktion leicht ersichtlich ist: for /F "tokens=2 delims== usebackq" %f in (`assoc .txt`) do @ftype %f
107
9 Ablaufsteuerung
Aus dem Output von assoc wird das zweite Wort extrahiert und beim Kommando der for-Schleife, ftype, diesem als Parameter übergeben. Nicht ganz so einfach wie bei Unix-Shells, da erst eine Schleife um das Kommando herumgebaut werden muss, aber dennoch sehr mächtig! In den Beispielskripten ab Seite 189 wird davon häufig Gebrauch gemacht.
9.2.9
Tipps & Tricks
Ausgaben Da bei Schleifen oft viele Kommandos ausgeführt werden, sollten Sie darauf achten, dass die Performance des Systems nicht unnötig ausgebremst wird, indem Befehle falsch gruppiert werden. So sehen die beiden Zeilen unten auf den ersten Blick fast gleich aus. Führt man die Kommandos aus, erzeugen sie beide auch die gleiche Ausgabe, eine Liste der Dateien, deren Erstellungsdatum im Mai liegt (z.B. 29.05.03 23:29; paper.zip). for %f in (*.zip) do @echo %~tf ; %f | find ".05." (for %f in (*.zip) do @echo %~tf ; %f) | find ".05."
Bei der ersten Schleife umfasst das Kommando aber sowohl das echo als auch das Pipelining an den find-Filter. Dieser wird pro gefundener Datei also einmal aufgerufen. Bei der zweiten Schleife dagegen wird über eine Gruppierung per Klammern erst die for-Schleife mit echo allein in einer Subshell abgearbeitet und dann die komplette Ausgabe einmal an den Filter find übergeben. Der Zeitunterschied durch den Wegfall der mehrfachen Filteraufrufe ist beträchtlich: auf einem meiner Rechner 8,3 Sekunden gegenüber nur 0,4 Sekunden bei der zweiten Lösung! Versuchen Sie also nach Möglichkeit immer, die Aufrufreihenfolge bzw. Filter und Umleitungen zu optimieren. Bevor Sie mir nun eine E-Mail schreiben, dass diese Suche auch wesentlich einfacher zu realisieren wäre (bei einem deutschen Windows über ein simples dir *.zip | find ".05."), gebe ich Ihnen Recht. Allerdings hätte ich dann kein Beispiel für den PerformanceUnterschied bei falscher Gruppierung mehr gehabt … Spaltentransformationen Zusätzlich kann eine Kombination von zwei for-Schleifen auch dazu dienen, mehrspaltigen Output in eine einspaltige Liste zu konvertieren. Ein Beispiel für eine solche Ausgabe liefert net group mit der Liste der Gruppen in einer Domäne. Wird der Vorspann der Datei entfernt (verwenden Sie dazu more und die Option /+), bleibt als Liste eine dreispaltige Ausgabe in der unten gezeigten Form übrig:
108
Das mächtige for *Administratoren *Domain Administratoren *Schema Administratoren *Azubis
Das folgende Kommando aus zwei geschachtelten for-Schleifen wandelt diese dreispaltige Liste in eine einspaltige Liste um: C:\tmp>for /F "tokens=1,2,3 delims=*" %f in (test.dat) do @for %m in ("%f" "%g" "%h") do @if not %m == "" echo %~m
Eine Zeile
Zuerst wird die Datei gelesen, und die Wörter werden jeweils am Stern aufgetrennt, wobei die ersten drei Wörter gelesen werden sollen. Diese werden als %f, %g und %h der zweiten Schleife übergeben. Da in den Namen der Gruppen auch Leerzeichen vorkommen können, werden diese sicherheitshalber in doppelte Anführungszeichen eingeschlossen. Das Kommando in der zweiten Schleife testet nun zuerst, ob ein Leerstring vorliegt (wie in der letzten Zeile der Beispielausgabe für die dritte Spalte). Ist dies nicht der Fall, wird das Wort ausgegeben, aber durch die Tilde im Variablennamen werden vorher die Anführungszeichen wieder entfernt. Hier noch die Ausgabe des obigen Kommandos. Administratoren Benutzer Training Domain Administratoren Vertrieb Entwicklung Schema Administratoren Server Operatoren Produktion Azubis Manager
Mit etwas Grübeln ist also auch eine Spaltentransformation kein Problem für die Shell. Dateinamen mit Leerzeichen Sollen Zeilen aus einer Datei mit einem Leerzeichen gelesen werden, wird die Sache etwas kompliziert. Überall werden diese Dateinamen nämlich mit umgebenden Anführungszeichen angegeben. Falls Sie das bei for-Schleifen versuchen, wird Sie das Ergebnis überraschen: for /F %f in ("Datei mit Blank.txt") do @echo %f
Als Ausgabe erhalten Sie immer das erste Wort aus dem Dateinamen. Dies liegt am Standardverhalten der Shell. Werte ohne Begrenzer werden als Liste von Werten oder Dateinamen aufgefasst. Ist die Werteliste in doppelte Anführungszeichen eingeschlossen (wie oben), wird dies zusammen mit der Option /F als direkte Zeichenkette für das
109
9 Ablaufsteuerung
Parsing behandelt, wie weiter oben in Abschnitt 9.2.7 erläutert. Wertelisten in einfachen Anführungszeichen werden dagegen auch ohne die Option usebackq als Befehl für das Backquoting (siehe Abschnitt 9.2.8) angesehen. Erst mit der Angabe der Option usebackq werden Wertelisten in Rückwärtsanführungszeichen (engl. backquotes) für das Backquoting verwendet. Ist diese Option angegeben, wird aber auch die Funktion der doppelten Anführungszeichen bei /F geändert. Diese geben nun einen Dateinamen mit Leerzeichen an. Um das Kommando von oben als wie ursprünglich beabsichtigt auszuführen, muss es wie folgt geändert werden: Eine Zeile
for /F "usebackq" %f in ("Datei mit Blank.txt") do @echo %f
Solle Ihnen das verwirrend vorkommen, geht es Ihnen wie nahezu allen Benutzern des for-Kommandos. Da aber die Kennzeichnung direkter Zeichenketten und Dateinamen mit Leerzeichen das gleiche Zeichen für ihre Begrenzung nutzen ("), ließe sich das Problem nur so auflösen. In der EDV wird so etwas dann gerne mit dem Ausdruck „historisch gewachsen“ bezeichnet …
110
10
Makros
10.1 Grundlagen Auch wenn viele Administratoren gerne zur Arbeitsersparnis die Shell benutzen (und dies nach der Lektüre dieses Buches hoffentlich noch viel mehr als vorher), so tippt doch niemand gerne immer wieder die gleichen Kommandos ein. Praktischerweise bietet die Shell eine einfache Möglichkeit, solche Wiederholungen dadurch zu vermeiden, dass Sie sich ein Makro definieren. Der Begriff Makro trifft den Sachverhalt allerdings nicht ganz, es handelt sich hier eher um einen Alias, da Sie durch die Verwendung des Alias das unter diesem Namen abgelegte Kommando aufrufen. Zuständig für diese Funktionalität ist das Kommando doskey. Die Expansion eines Makros (also das Ersetzen des Kommandos durch die definierten Befehle) erfolgt nur einmal, nicht rekursiv. Damit werden Probleme mit Definitionen wie der folgenden verhindert: doskey echo=time /t ^& echo $*
Hier wird ein Makro echo definiert, das den Shell-Befehl echo aufruft. Die Funktion folgt der Erwartung; es gibt keine Endlosschleife: c:\shellbuch\samples>echo Das ist ein Test 18:27 Das ist ein Test
Das Zeichen „^“ vor dem kaufmännischen UND wird benötigt, da die Befehlsgruppierung erst bei der Makroexpansion aktiviert werden soll und nicht bereits bei der Definition.
111
10 Makros
10.2 Arbeiten mit Makros Die Definition eines Makros wird vor der Befehlsausführung ausgelöst. Dies bedeutet, dass Makros Vorrang vor den Kommandos der Shell besitzen, Sie also mit einem Makro time den internen Befehl time der Shell überschreiben. Dies kann unter Umständen ganz nützlich sein, wenn Sie für ihre in Teilzeit administrierenden Kollegen bestimmte Kommando sperren oder mit zusätzlichen Sicherheitsabfragen versehen möchten, will aber bedacht sein. Zusätzlich erfolgt die Expansion eines solchen Makros (bedingt durch die Natur von doskey) nur bei einer interaktiven Eingabe des Kommandos. Die schlechte Nachricht lautet daher: keine Makros in Batchfiles verfügbar. Dies beschränkt den Einsatz der Makros wirklich auf Abkürzungen bei interaktiven Sitzungen.
10.2.1
Definieren, ändern, löschen
Definieren Zur Definition eines Makros benutzen Sie das Kommando doskey, gefolgt vom Namen des Makros, danach von einem Gleichheitszeichen und dem Kommandotext. Mit der folgenden Zeile rüsten Sie auf allen Maschinen vor Windows 2003 das Kommando whoami nach: doskey whoami=@echo %username%@%userdomain% (working on %computername%, authenticated by %logonserver%)
Nun können Sie whoami wie eines der vorhandenen Shell-Kommandos verwenden: C:\Shellbuch\Samples>whoami [email protected] (working on ORION, authenticated by \\RENNSEMMEL)
Ändern Eine erneute Definition eines gleich lautenden Alias überschreibt die alte Definition ohne vorherige Rückfrage. Eventuelle Leerzeichen im Makronamen bzw. vor dem Kommandotext werden ignoriert und führen nicht zu zwei verschiedenen Makronamen. Löschen Das Löschen einer Makrodefinition erfolgt wie das Löschen einer Umgebungsvariablen durch eine leere Zuweisung. doskey whoami=
Danach ist die Definition gelöscht. Auch hier erfolgt keine Rückfrage. C:\Shellbuch\Samples>whoami Der Befehl "whoami" ist entweder falsch geschrieben oder konnte nicht gefunden werden.
112
Arbeiten mit Makros
Damit ist in Grenzen auch eine Abfrage möglich, ob ein Makroname noch frei ist. Ein unbekannter Befehl wie im obigen Kommando liefert der Shell einen Errorlevel von 9009 zurück. Wird auf diesen Fehlercode gestestet, kann festgestellt werden, ob ein Makro geladen war und lief oder die obige Fehlermeldung erzeugt wurde.
10.2.2
Parameter für Makros
Warum benötigen Makros Parameter? Für ein Beispiel möchte ich auf die Variablenersetzung bei for-Schleifen (s. Seite 99) zurückkommen. Dort habe ich Ihnen die Nachbildung von which für die Windows Shell vorgestellt. for %f in (notepad.exe) do echo %~$PATH:f
Dieses Kommando muss nun nur noch in die Definition eines Makros mit doskey eingebunden werden und kann dann als neuer Befehl genutzt werden. doskey which=for %f in (notepad.exe) do echo %~$PATH:f
Problematisch ist allerdings, dass so immer nur gesucht werden kann, welche Datei ausgeführt wird, wenn Sie notepad.exe eingeben. Daher verfügt auch doskey über die Möglichkeit, für solche Makros Parameter zu definieren, die dann für den Dateinamen beliebige Werte erlauben. Diese Parameter werden mit den Platzhaltern $1 bis $9 angesprochen (ähnlich dem %1 bis %9 bei den Batchfiles). Der Platzhalter $T dient als Trennzeichen für mehrere Kommandos, während $* für die gesamte Eingabe dient. Hier nun die Definition des Makros mit einem Parameter für den Dateinamen: doskey which=for %f in ($1) do echo %~$PATH:f
Ein which freecell.exe würde also auch funktionieren, da der erste Parameter den Dateinamen darstellt und dieser über $1 in der Schleife gesucht wird. Mehr als neun Parameter sind allerdings nicht möglich, da im Gegensatz zu Batches keine Möglichkeit zum Shifting (s. Seite 121) existiert. Dennoch lässt sich mit Hilfe der Makros eine ganze Reihe von Definitionen für den interaktiven Betrieb erstellen. Beispielsweise existiert in manchen Unix-Shells ein automatischer Stapel für die per cd angewählten Verzeichnisse. Mit einem einfachen cd - kehren Sie zum jeweils letzten Verzeichnis zurück. Natürlich bietet auch die Windows Shell diese Funktionalität, allerdings in zwei Kommandos (pushd, pod), an die Sie sich auch noch erinnern müssen. Für meine Shell habe ich mir daher ein Makro definiert, das diese Funktionalität zusammen in einem „neuen“ cd nachbildet. doskey cd=if not $*! equ -! (pushd $*) else (popd)
113
10 Makros
Hier wird zuerst gestestet, ob die Argumente hinter dem Makronamen ungleich dem Minuszeichen sind. Falls ja, wird per pushd in das entsprechende Verzeichnis gewechselt. Falls nein, haben wir das Minuszeichen, und dementsprechend wird ein popd durchgeführt. Die nachfolgende Ausgabe zeigt den Einsatz des neuen Makros: C:\tmp>cd \DATA + C:\DATA>cd "\Dokumente und Einstellungen" ++ C:\Dokumente und Einstellungen>cd + C:\DATA>cd C:\tmp>
Zusätzlich habe ich hier den Prompt (siehe Abschnitt 3.3.2) so verändert (auf die Zeichenfolge $+ $P$G), dass ich den Zustand des Verzeichnisstapels mit einem Pluszeichen pro Ebene angezeigt bekomme. Seit ich dieses Makro im Einsatz habe, kann ich pushd und popd im Prinzip vergessen. Im Prinzip deshalb, weil ich mittlerweile auf fremden Maschinen instinktiv cd - eingebe und mich dann über die Fehlermeldung wundere …
10.3 Makros für bestimmte Programme Die Funktionalität von doskey-Makros kann jedes Programm nutzen, das sich Eingaben über die regulären Systemaufrufe des Betriebssystems holt. Aus diesem Grund können Sie beispielsweise auch bei ftp, nslookup oder anderen Konsolenprogrammen mit der Taste Cursornach-oben das letzte Kommando zurückholen. Der große Vorteil von doskey liegt nun darin, dass Makros auch ganz gezielt nur für ein bestimmtes Konsolenprogramm definiert werden können. Nur wenn das in der Makrodefinition angegebene Programm läuft, ist die Definition auch aktiv. Damit habe ich mir einen lästigen Unterschied in der Bedienung von Konsolenkommandos vom Hals geschafft: Überall wird ein Programm mit quit verlassen, egal ob ftp, telnet, netsh oder wie sie alle heißen. Nur bei nslookup muss ich exit eingeben. Mit doskey kein Problem: doskey /exename=nslookup.exe quit=exit
sorgt dafür, dass bei Eingaben ein quit durch exit ersetzt wird, wenn das Programm nslookup.exe ausgeführt wird. Denken Sie daran, dass diese Option für die Angabe des Programmnamens immer vor der eigentlichen Makrodefinition angegeben werden muss!
114
Makros speichern & laden
10.4 Makros speichern & laden Die Definition der Makros bleiben bis zum Schließen der Shell erhalten. Danach müssten Sie also alle Definitionen wieder eingeben, gäbe es da nicht eine einfache Möglichkeit, alle Definitionen in einer Datei zu sichern. Geben Sie dazu in der Kommandozeile das Kommando doskey /macros bzw. doskey /macros:all ein (auf den Unterschied der beiden Kommandos gehe ich gleich ein). Sie erhalten eine Liste aller derzeit definierten Makros, wie das folgende Beispiel zeigt: cd=if not $*! equ -! (pushd $*) else (popd) whoami=echo %username%@%userdomain% (on %computername%) test=cls && time /t
Diese Ausgabe kann per Ausgabeumleitung in eine Datei gespeichert werden und macht somit die Makrodefinitionen persistent. Über eine andere Option von doskey kann diese Liste wieder geladen und die Makros können aktiviert werden. Der Unterschied zwischen /macros und /macros:all liegt darin, dass bei der ersten Version nur die für die Shell definierten Makrodefinitionen ausgegeben werden, bei der zweiten Form dagegen alle vorhandenen Definitionen, wie die zweite Ausgabe zeigt: [nslookup.exe] quit=exit [cmd.exe] cd=if not $*! equ -! (pushd $*) else (popd) go=if not $*! equ -! (pushd $*) else (popd) test=cls && time /t
Das Format entspricht einer einfachen INI-Datei, sollte also keine großen Probleme bereiten, wenn Sie diese Datei mit einem anderen Programm bearbeiten oder ändern möchten. Geladen werden kann diese Definitionsdatei über das Kommando doskey /macrofile=
Alle eventuell bereits bestehenden Definitionen werden durch den Inhalt der Datei überschrieben. Es bietet sich an, beim Beenden der Arbeit zuerst die Definitionen mit den Makros in eine Datei zu sichern und anschließend die Shell zu beenden. Natürlich kann für diesen Zweck auch wieder das Kommando exit durch ein eigenes Makro überschrieben werden, das zuerst die Definitionen wegsichert und dann die Shell beendet. Diese Aufgabe überlasse ich Ihnen als Abschlussübung für dieses Kapitel. Alternativ kann eine Liste mit Makros auch beim Start der Shell geladen werden, indem Sie per AutoRun-Option in der Registry dort das Kommando zum Laden von Makros einbauen. Achten Sie aber darauf, dass keinerlei Output entsteht, da sonst bei jedem Start einer Shell oder Subshell (auch bei Filtern!) dieser Output mit bearbeitet wird!
115
10 Makros
10.5 Tastenkombinationen für doskey Die erweiterten Funktionen von doskey sind per Default bei der Shell aktiviert, und damit erhalten Sie einige Tastenkombinationen für die Bearbeitung der Kommandozeile, die vielen Benutzern noch unbekannt sind. Diese Liste habe ich Ihnen aus Gründen der Eingabeeffizienz gleich zu Beginn des Buches präsentiert, sehen Sie also einfach noch einmal kurz auf der Seite 31 nach.
116
11
Shell Scripts (Batches)
11.1 Einführung Auch bei Windows ist eine Möglichkeit vorhanden, mehrere Kommandos in einer Datei zusammenzufassen und diese dann ablaufen zu lassen. Während die Unix-Leute bei dieser Möglichkeit von sog. Shell Scripts sprechen, hat sich in Windows der Ausdruck Batchfiles eingebürgert. Gemeint ist in beiden Fällen das Gleiche: eine Reihe von Kommandos, die in einer Datei gespeichert ist, die durch die Angabe ihres Dateinamens gestartet und ausgeführt wird. Ich verwende die beiden Ausdrücke Shell Script und Batch(file) in diesem Buch synonym. Viele Aufgaben lassen sich sinnvoll nur mit einer Batchdatei lösen, da andernfalls immer wieder die Eingabe mehrerer Befehlszeilen nötig wäre. An dieser Stelle möchte ich Ihnen die Grundlagen der Erstellung von Shell Scripts für Windows erläutern. Weiterführende Themen wie Parameter, Ablaufsteuerung oder andere Aspekte werden später behandelt. Für den Anfang ist eine Batchdatei wirklich nur eine Datei mit einem Stapel von Befehlen, der der Reihe nach abgearbeitet wird.
11.2 Batchfiles 11.2.1
Aufbau
Batchfiles (oder auf Deutsch Stapeldateien) sind nicht formatierte Textdateien. Standardmäßig besitzen sie die Erweiterung .bat oder .cmd (beide sind völlig gleichwertig). Wie der Name bereits ausdrückt, enthalten sie eine Abfolge (einen Stapel) von Kommandos, die von der Shell ausgeführt werden. Die Shell liest so lange zeilenweise aus der Datei, bis ein Kommando gefunden wird. Wird das Ende der Datei erreicht, wird der Batch beendet. Mit Hilfe von Kontrollstrukturen lässt sich der logische Ablauf des Stapels steuern, so dass auch komplexe Aufgaben möglich sind. Als Entwicklungswerkzeug kann (bei genügender Leidensfähigkeit) auch der bordeigene Editor notepad.exe dienen, der allerdings nicht über großen Komfort verfügt. Moderne Editoren verfügen über die Möglichkeit der Syntaxhervorhebung, können Makros erstellen und
117
11 Shell Scripts (Batches)
ermöglichen das Anlegen von Textbausteinen zur Wiederverwendung von Code25.
11.2.2
Syntax
Batches sind zeilenweise aufgebaut. Jede Zeile kann einen der folgenden Inhalte haben: 왘 Leerraum
Leerzeilen werden von der Shell überlesen, sind aber zur Gliederung des Scripts ein sinnvolles Hilfsmittel. 왘 Kommentar
Kommentare werden nachfolgend detaillierter erläutert. 왘 Befehlszeile
Als Befehle zählen alle internen und externen Kommandos, jeder Programmaufruf oder der Aufruf eines anderen Batches. 왘 Sprungzieldefinition
Die Sprungzieldefinition dient der Ablaufsteuerung des Batches.
11.2.3
Kommentare in Batches
Die einfachste Möglichkeit, einen Kommentar zur Dokumentation einzubauen, besteht in der Verwendung des „Befehls“ rem. (für remark, Anmerkung). Dieses Kommando hat keinerlei Auswirkungen, es dient lediglich dazu, den Rest der Zeile als Kommentar zu kennzeichnen. Alternativ dazu kann eine Zeile auch mit zwei Doppelpunkten begonnen werden. Diese Zeilen werden bei der Ausführung des Batches nicht angezeigt, auch wenn die Ausgabe der Befehlszeilen eingeschaltet ist. Daher wird diese Art der Kommentarerstellung bevorzugt verwendet. Gewöhnen Sie sich am besten gleich zu Beginn an, Ihre Batches mit Leerzeilen zu strukturieren und mit genügend Kommentaren den Ablauf und die Funktion des Batches zu beschreiben. Nichts ist frustrierender und langweiliger, als sich nach sechs Monaten mühevoll wieder in den eigenen Code einarbeiten zu müssen, den man bei der Erstellung als so einfach ansah, dass jeder Kommentar Zeitverschwendung gewesen wäre. Beschreiben Sie daher in den Kommentaren auch nicht, was der Batch tut — das ist aus dem Code ersichtlich. Beschreiben Sie lieber, warum der Code das tut, was er tut. Diese Informationen sind es, die die Einarbeitung in fremden Code erleichtern. 25 Ich werde an dieser Stelle aus Erfahrung bei anderen Gelegenheiten keinen Editor empfehlen, da sich so leicht Religionskriege starten lassen, gegen die die Kreuzzüge wie ein missglücktes Familienpicknick wirken.
118
Parameterchecks
11.2.4
Parameter
Jedes Shell Script kann bei Bedarf auch Parameter erhalten, von einer einfachen Option über Dateinamen bis zu einem ADSI-Pfad. Diese Parameter werden dem Batch übergeben und stehen über besondere Platzhalter zur Verfügung. Die Kombination %1 steht für den ersten übergebenen Parameter, %2 für den zweiten und so weiter bis %9 für das neunte Argument. Wie Sie mehr als neuen Parameter übergeben, auf leere Parameter testen oder diese Parameter konvertieren, erfahren Sie weiter unten im Kapitel 11.3, „Parameterchecks“.
11.2.5
Ausführen von Shell Scripts
Für die Ausführung von Shell Scripts (Batches) reicht die Eingabe des Namens, da die Erweiterungen .bat und .cmd als ausführbare Dateien registriert sind. Wie Sie dies feststellen und gegebenenfalls auch ändern, habe ich im Kapitel über PATHEXT geschildert. Ein Shell Script kann selbst wieder andere Shell Scripts starten. Hierbei müssen Sie allerdings entscheiden, was mit dem aufrufenden Shell Script geschehen soll. Findet sich in einer Zeile nur der Aufruf eines anderen Shell Scripts über den Dateinamen, wird der gerade ausgeführte Batch beendet, und die Kontrolle wird dem aufgerufenen Shell Script übergeben. Wird vor dem Namen des aufgerufenen Shell Scripts das Schlüsselwort CALL angegeben, so wird dieses Shell Script ausgeführt, kehrt aber nach der Beendigung zum aufrufenden Batch zurück.
11.3 Parameterchecks 11.3.1
Anzahl und Art der Parameter
Für viele Shell Scripts sind Parameter notwendig, die erst die für die Ausführung notwendigen Daten liefern. Wie bereits oben erläutert, stehen dafür die Platzhalter %1 bis %9 zur Verfügung. Diese werden innerhalb des Scripts an der Stelle eingesetzt, an der die Parameter verwendet werden. Dies kann auch mehrmals geschehen. Eine Änderung der in %1 bis %9 enthaltenen Werte ist nicht möglich. Falls Sie die übergebenen Daten eventuell verändern wollen, müssen Sie dies über den Umweg einer Umgebungsvariablen tun. Möchten Sie auf die gesamte Parameterliste zugreifen, so existiert hierfür der Platzhalter %*. Genau wie bei Dateinamen gilt auch hier, dass Parameterwerte mit Leerzeichen in doppelte Anführungszeichen eingeschlossen werden müssen.
119
11 Shell Scripts (Batches)
Auch für die Parameter in einem Shell Script existiert die erweiterte Variablenersetzung wie bei den Umgebungsvariablen. Der einzige Unterschied liegt darin, dass Sie das Prozentzeichen nur zu Beginn des Parameters angeben und als letztes Zeichen die Nummer des Parameters. Tabelle 11.1: Erweiterte Variablenersetzung für BatchParameter
Variable
Beschreibung
%~1
entfernt etwaige umgebende Anführungszeichen aus %1
%~f1
erweitert %1 zu einer vollständig qualifizierten Pfadbezeichnung
%~d1
erweitert %1 zu einem Laufwerksbuchstaben
%~p1
erweitert %1 zu einem Pfad
%~n1
erweitert %1 zu einem Dateinamen
%~x1
erweitert %1 zu einer Dateinamenerweiterung
%~s1
erweitert den Pfad, so dass nur der kurze Dateiname enthalten ist
%~a1
erweitert %1 zu Dateiattributen
%~t1
erweitert %1 zu den Datums-/Uhrzeitangaben der Datei
%~z1
erweitert %1 zu der Größe der Datei
%~$PATH:1
durchsucht die in der Umgebungsvariablen PATH aufgeführten Verzeichnisse und erweitert %1 zu dem vollständig qualifizierten Namen des ersten gefundenen Verzeichnisses. Wurde der Name der Umgebungsvariablen nicht festgelegt oder die Datei nicht gefunden, gibt der Parameter eine leere Zeichenfolge zurück.
Einige dieser Erweiterungen funktionieren natürlich nur dann, wenn es sich bei dem Parameter auch um eine existierende Datei handelt. Ist dies nicht der Fall, so expandieren diese Erweiterungen zu einer leeren Zeichenkette. Was ich übrigens immer noch vermisse: eine einfach Möglichkeit, die Anzahl der Parameter zu bestimmen (ähnlich $# in manchen Skriptsprachen). Der Parameter %0 Zusätzlich zu den Parametervariablen existiert noch ein Platzhalter %0, der für den Batch selbst steht, also den Namen des Shell Scripts enthält. Nur so sind Selbstaufrufe (s.u.) möglich, da Sie so den Namen des Shell Scripts erfahren. Der Inhalt von %0 entspricht immer genau dem Aufruf des Shell Scripts auf der Kommandozeile. Sollten Sie also innerhalb des Batches das aktuelle Verzeichnis wechseln, kann es sein, dass ein Selbstaufruf nicht mehr einwandfrei funktioniert, da der Benutzer unter Umständen nur den Dateinamen ohne Pfad einge-
120
Parameterchecks
geben hat. Einen Ausweg bietet auch hier die erweiterte Variablenersetzung. Ersetzen Sie einfach %0 durch %~f0, und Sie erhalten den kompletten Dateipfad für das Shell Script.
11.3.2
Mehr als neun Parameter mit shift
Sollten Sie Ihrem Shell Script mehr als neun Parameter mit auf den Weg geben, existieren dafür keine Platzhalter mehr. Abgesehen davon, dass ich bei dieser Anzahl von Parametern über andere Formen der Datenübergabe nachdenken würde (z.B. eine Parameterdatei oder die Registry), können Sie auf diese Werte dennoch zugreifen. Das Kommando shift schiebt alle Parameter um eine Position nach links. Möchten Sie erst ab einer bestimmten Parameternummer schieben, geben Sie diese Zahl nach Shift als Option an, beispielsweise shift /3. Dies bewirkt, dass %1 und %2 erhalten bleiben, aus %4 wird danach der neue %3, und der alte Inhalt von %3 geht verloren. Als Beispiel unten ein Shell Script, das einfach nur seine Parameter per echo ausgibt: @echo off echo Parameter 1 = %1 echo Parameter 2 = %2 echo Parameter 3 = %3 echo Parameter 4 = %4 echo. echo Jetzt ab Position 2 shiften .... shift /2 echo Parameter 1 = %1 echo Parameter 2 = %2 echo Parameter 3 = %3 echo Parameter 4 = %4
Jetzt wird dieses Script mit einer Befehlszeile mit Parametern aufgerufen: c:\tmp>test Demo "Parameter mit Blanks" /x 1234 Parameter Parameter Parameter Parameter
Damit sind auch mehr als neun Parameter möglich, und Sie sind in der Lage, bestimmte Parameter zu Beginn der Kommandozeile von der Verschiebung auszunehmen. Allerdings ist dies noch keine besonders
121
11 Shell Scripts (Batches)
flexible Parameterüberprüfung. Was ist beispielsweise, wenn der Benutzer eine Option statt als /debug als /DEBUG angibt und Sie nur die kleingeschriebene Variante testen? Im folgenden Abschnitt möchte ich daher eine etwas flexiblere Möglichkeit der Parameterüberprüfung vorstellen.
11.3.3
Flexible Parameterprüfung
Test auf leere Parameter Der Test, ob beim Aufruf des Scripts ein Parameter überhaupt angegeben wurde, ist eine der grundlegenden Überprüfungen vor der Ausführung irgendwelcher Aktionen. Die in der Literatur oft angegebene Standardlösung führt aber dennoch immer wieder zur Problemen. In den meisten Fällen sieht der Code nämlich wie folgt aus: if <parm> == <wert> :: beispielsweise if %1 == build goto actBuild
Problematisch wird die Sache dann, wenn kein Parameter beim Aufruf des Scripts angegeben wird. Im Beispiel oben ergibt sich dann ein syntaktischer Fehler, der den Batch abbricht. Ist %1 leer, sieht die Zeile so aus: if == build goto actBuild
Aus diesem Grund empfehlen viele Beispielskripte in Zeitschriften, den Parameter zu klammern, also links und rechts davon ein Zeichen zu setzen, so dass auch bei einem leeren Parameter ein Vergleich möglich ist. if [%1] == [build] goto actBuild :: bei leerem Parameter immer noch korrekte Syntax if [] == [build] goto actBuild
Aber auch diese Abfrage ist nicht hundertprozentig sicher, da führende oder nachfolgende Leerzeichen den Vergleich scheitern lassen. Sorgen Sie also immer dafür, dass umschließende Leerzeichen nicht auftreten, oder fügen Sie nur vor dem Parameter ein Zeichen an. Diese Technik gilt auch bei Umgebungsvariablen. Stellen Sie sich vor, Sie erfragen per set /p den Wert einer Variablen in einem Batch. Nun gibt der Benutzer aus Versehen noch ein nachfolgendes Leerzeichen mit ein. Dies zählt mit zum Inhalt der Variablen, und bei einem [%var%] schlägt dann ein Vergleich mit einem Kommando (z.B. [make]) fehl, da das Leerzeichen noch zwischen Befehlswort und schließender Klammer steht.
122
Ablaufsteuerung
Test auf Parameter an beliebiger Position Geht es nur darum festzustellen, ob eine Option angegeben wurde oder nicht (unabhängig von der Schreibweise), lässt sich dies relativ mit for lösen. In ihrer einfachsten Form läuft die Schleife einmal durch jedes Element der Werteliste in den runden Klammern. Warum also nicht mit %* die gesamte Parameterliste an eine Schleife übergeben und mit Hilfe von if auf die gesuchte Option testen? Die Möglichkeit von if, mit /i die Schreibweise zu ignorieren, spart Ihnen das Absuchen aller möglichen Schreibweisen und macht das Script leichter benutzbar. Dem folgenden Beispiel können Sie entnehmen, wie leicht sich das Vorhandensein einer Option /debug (egal ob groß, klein oder gemischt geschrieben) realisieren lässt. @echo off setlocal set debug= for %%f in (%*) do if /i %%f == /debug set debug=1 if defined debug echo Debug-Mode echo Dies wird immer ausgegeben. endlocal
Listing 11.1: Flexible Abfrage von Parametern
11.4 Ablaufsteuerung Wenn nichts anderes programmiert wurde, beginnt die Ausführung des Scriptcodes mit der ersten Zeile, die kein Kommentar oder keine Sprungzieldefinition ist. Das Script endet, wenn die letzte Zeile der Scriptdatei erreicht wurde. Da eine rein lineare Ausführung in den wenigsten Fällen ausreicht, existieren auch bei Shell Scripts Möglichkeiten, den Ablauf des Scripts zu steuern.
11.4.1
Verzweigungen
Bei Verzweigungen innerhalb von Shell Scripts gilt das Gleiche wie bei if für die interaktive Eingabe. Lesen Sie hierzu bitte im Abschnitt 9.1 nach.
11.4.2
Schleifen
FOR Schleifen dienen dazu, Kommandos mehrfach zu wiederholen. Dies kann in Form eines for-Kommando (s. Seite 96) erfolgen oder in Form einer manuell kodierten Schleife. Von for haben Sie sicher bereits im Abschnitt 96 genug gehört, so dass ich hier nur nochmals auf die einzige Besonderheit hinweisen möchte, wenn Sie for in einem Shell Script einsetzen: Die Prozentzeichen der Schleifenvariablen müssen beim Einsatz in einem Batch verdoppelt werden.
123
11 Shell Scripts (Batches)
Aus der Zeile for %f in (*.txt) do @if %~zf GTR 1048576 echo Grosse Datei
wird innerhalb des Shell Scripts die Zeile for %%f in (*.txt) do @if %%~zf GTR 1048576 echo Grosse Datei
Schleifen mit if und goto Bei einer handkodierten Schleife wird mit Hilfe einer if-Abfrage die Testbedingung geprüft und dann ggf. per goto zu einem definierten Sprungziel verzweigt. Ein möglicher Einsatzzweck dieser manuell kodierten Form einer Schleife kann beispielsweise die Feststellung des Pfadnamens einer Datei sein. Dieser lässt sich feststellen, indem vom Ende des Dateinamens aus nach einem Backslash gesucht wird. Dieser trennt dann den Dateinamen vom kompletten Pfad ab. Dafür kann for nicht benutzt werden, das entsprechende Shell Script ist aber auch so relativ kurz. Die Zeilennummern dienen nur der Orientierung, sie gehören natürlich nicht zum Batch. Üblicherweise wird für solche Shell Scripts die Kommandoanzeige per echo off ausgeschaltet, ich habe dies hier weggelassen, damit Sie die Ausführung in einem Konsolenfenster besser verfolgen können. Listing 11.2: Feststellen des Pfadanteils eines Dateinamens
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
:: getfilepath.bat set datei=%~1 :check :: letztes zeichen holen if not defined datei goto nopath if "%datei:~-1%" == "\" goto fertig :: ein zeichen kürzen set datei=%datei:~0,-1% goto check :nopath echo Kein Pfadanteil vorhanden goto eof :fertig echo Der Pfad lautet: %datei% :eof
Nachdem der erste Parameter (der Dateiname) der Umgebungsvariablen datei zugewiesen wurde, wird in Zeile 6 die Testbedingung formuliert. Mit dieser Variablen datei wird nun im Batch gearbeitet, der Parameter 1 (%1) bleibt für den Zugriff auf den originalen Dateinamen immer erhalten. Falls das letzte Zeichen des Inhalts von datei ein Backslash ist, kann die Schleife per Sprung an die Marke fertig verlassen werden. Ist dies nicht der Fall, wird der Inhalt der Variablen von rechts um ein Zeichen gekürzt. Dies geschieht per erweiterter Variablenersetzung in der Zeile 8. Danach wird in der Zeile 9 wieder zur Marke check gesprungen, die als nächstes Kommando wieder zur Testbedingung führt.
124
Ablaufsteuerung
Nach einem Aufruf mit dem Parameter C:\Shell\Demos\test.dat lautet die Ausgabe: Der Pfad lautet: C:\Shell\Demos\
Vielleicht fragen Sie sich jetzt, wozu die Zeile 5 dient. Dies ist eine Sicherheitsabfrage, falls als Parameter ein Dateiname ohne Pfadanteil übergeben wird. Dann wird nie ein Backslash gefunden, die Variable aber immer wieder in der Schleife um ein Zeichen gekürzt. Irgendwann ist nichts mehr da zum Kürzen, und die Zeile sieht zur Laufzeit dann so aus set datei=
Damit wird die Variable gelöscht, und die Abfrage in der Zeile 5 testet genau diese Bedingung. Ist die Variable datei nicht mehr definiert, hat sie keinen Inhalt mehr. Dies bedeutet, dass kein Backslash gefunden wurde und der ursprüngliche Dateiname auch keine Pfadinformationen enthält.
11.4.3
Unterroutinen
Die Windows Shell verfügt über keine besonders ausgefeilten Möglichkeiten zur Modularisierung von Shell Scripts. Dennoch lassen sich zumindest einfach Unterroutinen erstellen. Für eine weitergehende Diskussion zum Thema Modularisierung lesen Sie bitte den Abschnitt 12.4. Bereits seit MS-DOS besteht die Möglichkeit der Verwendung des Kommandos call, um eine zweite Batchdatei aufzurufen. Nachdem die Ausführung diese Datei beendet wurde, kehrt die Ausführung zur aufrufenden Datei zurück. Ein Nachteil dieses Ansatzes war allerdings die Tatsache, dass dafür eine externe Datei aufgerufen werden musste. Viele Administratoren versuchten, durch eine geschickte Kombination von Selbstaufruf und Parametern (s. Kapitel 11.5.1) diese Beschränkung zu umgehen. Seit den Kommandoerweiterungen von Windows 2000 existiert eine einfachere Möglichkeit, mit einer Datei auszukommen, in der einzelne Unterroutinen versammelt sind. Dazu werden vor jeder Unterroutine Sprungmarken definiert. Diese werden dann mit der neuen Syntax call :label angesprungen (achten Sie auf den Doppelpunkt vor der Sprungmarke). Unten ein Beispiel für diese Möglichkeit.
125
11 Shell Scripts (Batches) Listing 11.3: Verwendung von call :label
@echo off echo Shell Script laeuft ... call :makeDC kiste.firma.de echo Ergebnis: %result% :: muss sein, damit die subroutine :: nicht noch mal ausgefuehrt wird goto :eof :makeDC echo Das hier ist eine Unterroutine set result=%1 set result=%result:.=,dc=% set result=dc=%result%
Der fett markierte Bereich im Listing zeigt den Aufruf der Unterroutine. Dieser Aufruf erzeugt einen neuen Batchkontext, dessen Ausführung sofort an der angegebenen Sprungmarke beginnt. Falls Sie so etwas noch für Windows NT4 oder Windows 98 benötigen, hier der Batch in der Oldtimer-Variante. Eine Erläuterung dieses Selbstaufrufs finden Sie im nächsten Abschnitt des Buches. @echo off if !%1 == !JUMP shift && shift && goto %2 %3 %4 %5 %6 %7 %8 %9 echo Shell Script laeuft ... call %0 JUMP makeDC kiste.firma.de echo Ergebnis: %result% :: muss sein, damit die subroutine :: nicht noch mal ausgefuehrt wird goto :eof :makeDC echo Das hier ist eine Unterroutine set result=%1 set result=%result:.=,dc=% set result=dc=%result% Listing 11.4: Der Batch in der NT4-tauglichen Version
Wie Sie unschwer erkennen, ist die seit Windows 2000 mögliche Syntax wesentlich einfacher. In der NT4-Version müssen auch noch zweimal per shift die Parameter geschoben werden, da die Selbstaufrufmarkierung JUMP und die Sprungmarke makeDC wieder aus der Parameterliste verschwinden müssen. Sobald die Ausführung dieses neuen Batchkontextes das Ende des Scripts erreicht hat, wird die Scriptausführung nach dem aufrufenden call im ursprünglichen Kontext fortgesetzt. Für die Rückgabe von Funktionswerten bei Unterroutinen benutzen Sie am besten wie im obigen Beispiel Umgebungsvariablen.
126
Selbstaufruf
11.4.4
Shell Scripts beenden
Ein Shell Script wird im einfachsten Fall dann beendet, wenn kein Code mehr vorhanden ist. Falls Sie aus Gründen der Ablauflogik ein Shell Script direkt beenden müssen, haben Sie drei Möglichkeiten: 1. Sie definieren am Ende des Scripts eine Sprungmarke (z.B. :eof) und springen dann per goto (hier: goto eof) an diese Marke. Diese Methode hat den Vorteil, auch mit Windows NT4 zu funktionieren. 2. Sie nutzen die Befehlserweiterungen ab Windows 2000 und beenden den Batch mit dem Kommando goto :eof. Beachten Sie den Doppelpunkt vor der Sprungmarke. Dies ist eine Erweiterung des Sprungkommandos (ähnlich call :label) ab Windows 2000. 3. Sie verwenden (ebenfalls ab Windows 2000) das Kommando exit mit der Option /B. Ein exit /b beendet in einem Shell Script nur den gerade laufenden Batch. Außerhalb davon wird aber nach wie vor die gerade laufende Shell beendet! Der Vorteil dieser Methode liegt darin, dass Sie bei exit einen errorlevel als Parameter angeben können, der dem aufrufenden Programm oder Shell Script übermittelt wird.
11.5 Selbstaufruf Wieso sollte sich ein Shell Script selbst aufrufen? Diese Frage hat prinzipiell die gleichen Antworten wie bei den meisten Programmiersprachen: rekursive Lösungen von Problemen. Bei Batches kommt noch ein weiterer Aspekt dazu. Erst mit den Befehlserweiterungen von Windows 2000 und aufwärts ist eine halbwegs vernünftige Modularisierung von Batches in einer Datei möglich. Vorher blieb nur der Selbstaufruf und die anschließende Verteilung per Sprung an die korrekte Codestelle. Viele Shell Scripts sind auch heute noch nach diesem Muster gestrickt, weshalb ich dieses zuerst erläutern möchte. Danach werde ich Ihnen die für Windows 2003 zeitgemäßere Lösung vorstellen.
11.5.1
Aufruf mit call %0
Vor Windows 2000 war dies die einzige Möglichkeit, einen Batch rekursiv, also selbstaufrufend zu gestalten. Falls ein Shell Script sich selbst aber aufruft, muss eine Erkennung möglich sein, dass dieser Aufruf nun programmgesteuert und nicht mehr durch den Benutzer erfolgt. Dies geschieht meist durch einen speziellen Wert als ersten Parameter, der als Markierung dient und der dann gleich zu Beginn des Shell Scripts abgefragt wird. Bei allem stellt sich aber die Frage: Wozu überhaupt das Ganze? Die Antwort ist relativ einfach. In vielen Fällen lässt sich so eine Aufteilung der Lösung in mehrere Batches vermeiden. Abgesehen von der Platzverschwendung durch die Clustergrößen heutiger Festplatten müssen Sie bei der Weitergabe auch daran denken, alle Teilskripte zu kopieren.
127
11 Shell Scripts (Batches)
Als praxistaugliches Beispiel dient für dieses Kapitel ein Shell Script, dass während eines meiner Shell-Trainings entstand. Damit lässt sich die Reihenfolge der Zeilen einer Textdatei umdrehen, um beispielsweise die Zeilen am Anfang einer Datei nach hinten zu bringen. Hier erst einmal der Code für den Aufruf mit call %0, die Erläuterung des Ablaufs folgt nach dem Listing (die Zeilennummern gehören nicht zum Code und dienen nur der Erläuterung!). Listing 11.5: Umdrehen der Zeilen einer Textdatei.
128
01 :: reverse a text file, slow but plain shell 02 03 :: test der parameter 04 if !%1 == ! goto nofile 05 06 :: ist die datei auch da? 07 if not exist %1 goto nofile 08 09 :: test auf umgebungsvariable 10 if not defined temp goto noenv 11 12 :: haben wir uns aufgerufen? 13 if !%1 == !@@@ goto procline 14 15 :: evtl. bestehende datei loeschen 16 if exist %temp%\tmp.txt del %temp%\tmp.txt 17 18 :: 19 for /F "tokens=* usebackq" %%f in (%1) do call %0 @@@ "%%f" 20 more < %temp%\tmp.txt > %1 21 del %temp%\tmp.txt 22 del %temp%\line.txt 23 goto eof 24 25 :procline 26 echo %~2 > %temp%\line.txt 27 more < %temp%\tmp.txt >> %temp%\line.txt 28 more < %temp%\line.txt > %temp%\tmp.txt 29 goto eof 30 31 :noenv 32 echo Variable TEMP nicht definiert, Abbruch. 33 goto eof 34 35 :nofile 36 echo Keine Datei angegeben, Abbruch. 37 goto eof 38 39 :notfound 40 echo Die Datei %1 existiert nicht, Abbruch. 41 goto eof 42 43 :eof
Selbstaufruf
Das Script funktioniert prinzipiell folgendermaßen: 왘 Die Schleife in Zeile 19 läuft durch alle Zeilen der Eingabedatei, deren
Name als %1 übergeben wird (Test auf Vorhandensein Zeile 3–7). 왘 Für jede Zeile ruft sich der Batch wieder selbst auf, mit "@@@" als
Kennung für den ersten Parameter. Als zweiter Parameter wird der Inhalt der jeweiligen Zeile übergeben. 왘 Durch den Test in Zeile 13 springt der Ablauf beim Selbstaufruf
sofort zum Label in Zeile 25. 왘 Jetzt wird der Inhalt der Zeile in eine temporäre Datei line.txt
geschrieben, der Inhalt der Datei tmp.txt (die die bisherigen Zeilen enthält) wird dahinter angefügt. 왘 In der Zeile 28 wird diese Datei line.txt wieder nach tmp.txt
geschrieben, so dass diese für den nächsten Durchgang wieder zur Verfügung steht. Testen Sie diesen Ablauf mit einer einfachen Textdatei, die drei Zeilen Text enthält.
11.5.2
Aufruf mit CALL :label
Wie bereits im Abschnitt 11.4.3 erläutert, ist seit Windows 2000 auch der direkte Aufruf mit call :