Tales from the Script

Juli 2004

Veröffentlicht: 19. Jul 2004
*

Gute Argumente beim Scripting

Haben Sie schon einmal von diesen alle 17 Jahre auftretenden Zikadenschwärmen gehört? Bei Zikaden handelt es sich um Insekten, die nur einen Sommer lang auftreten, einen Höllenlärm und eine Menge Aufregung produzieren und dann wieder in der Versenkung zu verschwinden – nur um 17 Jahre später erneut aufzutauchen. Man könnte mit ruhigem Gewissen behaupten, dass wir nicht nur die Microsoft Scripting Guys, sondern auch eine Art Microsoft Scripting-Zikaden sind: Wir tauchen auf, machen eine Menge Lärm und verursachen viel Aufregung und verschwinden wieder. Und dann – siebzehn Jahre später – sind wir plötzlich wieder zurück. Wird das in diesem Zyklus weitergehen? Wer weiß? Am besten rufen Sie im Jahr 2021 die Technet Website auf, und sehen selbst nach.

Mit anderen Worten: Ja, wir wissen, dass es schon eine Weile seit dem letzten Tales from the Script -Artikel her ist. Es gibt eine Menge guter Gründe dafür (wer hat denn da gesagt: "Zum Beispiel, dass ihr einfach faul seid"?). Naja – wichtig ist nicht, wo wir gewesen sind, sondern nur, dass wir mit einem neuen, coolen Script Center wieder da sind. In diesem Moment sind wir noch nicht ganz sicher, welche der neuen Features bereits umgesetzt sind. Das Script Center wird aber in Kürze auf jeden Fall zum Beispiel um folgendes erweitert:

Hunderte von neuen Scripten

Links zu Webcasts, Scripting-Artikeln, Scripting-Kolumnen und mehr

Ein neues Solutions-Center mit fertigen, von den Microsoft Produktteams entwickelten Scripten für Unternehmensumgebungen

Das Script Writer’s Toolkit und das Script Writer’s Bookshelf

Eine neue tägliche FAQ-Kolumne mit dem Namen Hey, Scripting Guy!

Und oben drauf packen wir alle 17 Jahre noch eine neue Tales From the Script-Kolumne. Versprochen!

Ok – in Wirklichkeit wird es natürlich jeden Monat eine neue Ausgabe der Tales from the Script -Kolumne geben. Sie soll wieder zu einem primären Anlaufpunkt für Scriptentwickler werden, über den diese einfache und leicht zu verstehende Antworten auf die dringendsten Fragen finden. Zum Beispiel: "Wieso habt ihr Typen so lange gebraucht, um eine so kleine Scripting-Kolumne zu schreiben?"

Zum SeitenanfangZum Seitenanfang

Hinzufügen von Kommandozeilenargumenten zu Scripten

Wir haben uns gedacht, diesen Monat besprechen wir mal, wie Sie Ihren Scripten Kommandozeilenarguemente hinzufügen können. (Ja, das ist eine gute Idee für eine Kolumne. Sie brauchen uns aber nicht sofort zu danken – denn im Grunde haben Sie dazu ja noch 17 Jahre Zeit.) Wie Sie möglicherweise bereits festgestellt haben, sind die meisten Scripte im Script Center so entworfen, dass Sie gegen einen einzelnen Computer ausgeführt werden. Außerdem tendieren wir dazu, den Namen des entsprechenden Computers direkt im Script einzutragen (hardcodieren). Hier ist zum Beispiel ein Script, das nur ein einzige Sache macht: Es hält den Warndienst auf dem lokalen Computer an. Bei genauerer Betrachtung werden Sie sehen, dass wir den entsprechenden Computer (.) und den Namen des Dienstes (Alerter) fest in das Script eingetragen haben:

strComputer = "." Set objWMIService = GetObject _  
  ("winmgmts:\\" & strComputer & "\root\cimv2")
Set colServices = objWMIService.ExecQuery _  
  ("Select * FROM Win32_Service WHERE Name = 'Alerter'")
For Each objService in colServices
  objService.StopService
Next

Ein Script, das nichts macht, außer den Warndienst auf dem lokalen Computer zu beenden, ist wohl nicht sonderlich nützlich oder beeindruckend. Warum schreiben wir also überhaupt solche Scripte? Dafür gibt es zwei Gründe: Erstens bleiben die Scripte durch die Hardcodierung von Werten kurz. Unsere Scripte sind zum Lernen gedacht – wir wollen sie nicht mit zusätzlichem Programmcode voll stopfen, sondern Ihnen jeweils eine spezielle Aktion demonstrieren. Darum implementieren wir normalerweise auch keine Fehlerbehandlung in unseren Scripten. Für unsere Zwecke ist ein kurzes Script praktischer. Nebenbei verringert sich der Zeitaufwand für die Erstellung der Scripte (können Sie sich vorstellen, wie lange wir für komplexere Scripte brauchen würden?).

Außerdem wird es mit einfachen Scripts für Sie einfacher, unsere Scripts für Ihre eigenen Bedürfnisse anzupassen. Nehmen wir zum Beispiel einmal an, wir würden alle unsere Scripte so schreiben, dass Computernamen aus einer Textdatei eingelesen, einige Aufgaben ausgeführt und die Ergebnisse dann in einer Excel-Datei gespeichert werden. Das wäre sicher grundsätzlich völlig in Ordnung – es dürfte jedoch nicht mehr so einfach sein, das Script so anzupassen, dass die Computernamen aus Active Directory gelesen werden und die Ergebnisse im Kommandozeilenfenster ausgegeben werden. Je komplizierter wir die Scripte gestalten, desto schwerer ist es für Sie, diese an Ihre Anforderungen anzupassen. Das würde bedeuten, dass wir statt Scripten zum Lernen eher fertige Lösungen anbieten – und glauben Sie uns, niemand möchte das (zumindest dann nicht, wenn Sie nicht 17 Jahre auf eine Lösung warten möchten).

Grundsätzlich ist dieser Ansatz sehr effektiv. Wir müssen allerdings zugeben, dass wir die schlechte Angewohnheit haben, Dinge zu sagen wie "Klar, dieses Script ist nur ein Grundgerüst. Sie können es aber ganz einfach anpassen und beispielsweise Kommandozeilenargumente implementieren". Das stimmt auch – wir haben anscheinend nur nie jemandem erklärt, wie diese Anpassung der Scripte zum Beispiel mit Kommandozeilenargumenten gemacht wird. Diese Lücke werden wir nun füllen (und Sie haben gesagt, wir wären "faul"...).

Zum SeitenanfangZum Seitenanfang

Also, was sind noch mal gleich Kommandozeilenargumente?

Nur um sicher zu gehen, dass wir alle von der gleichen Sache reden, hier erstmal eine kurze Definition: Kommandozeilenargumente sind zusätzliche Informationen, die an ein Utility (ein Script, eine ausführbare Datei, was auch immer) bei dessen Start übergeben werden. Nehmen wir zum Beispiel einmal an, Sie versuchen die IP-Adresse 192.168.1.1 anzupingen. In diesem Fall werden Sie wohl so etwas in der Kommandozeile eingeben:

ping 192.168.1.1

Wie Sie vielleicht schon vermutet haben, ist 192.168.1.1 ein Kommandozeilenargument (auch Parameter, Schalter oder einfach nur Argument genannt). Brauchen Sie Kommandozeilenargumente überhaupt? Tja – nicht unbedingt. Nehmen Sie aber zum Beispiel einmal an, Sie könnten ping.exe keine IP-Adresse als Argument übergeben. Welchen Nutzen hätte das Tool dann wohl noch? Der Vorteil von Ping ist, dass Sie jede beliebige IP-Adresse anpingen können – und zwar deshalb, weil die IP-Adresse nicht hardcodiert ist.

Das gleiche gilt für Ihre Scripte. Wenn Sie den Computernamen hardcodieren, dann arbeitet das Script nur mit diesem einen Computer. Was passiert, wenn Sie das Script gegen einen anderen Computer ausführen möchten? Tja, in diesem Fall müssen Sie entweder das vorhandene Script bearbeiten oder ein total neues Script für den zweiten Computer erstellen. Wir brauchen wohl nicht extra zu erwähnen, dass dies nicht die effizienteste Arbeitsweise ist. Ein besserer Ansatz wäre es, ein einzelnes Script zu erstellen, das gegen jeden Computer ausgeführt werden kann. Hier kommen die Kommandozeilenargumente ins Spiel.

Uns ist klar, dass einige Leser an diesem Punkt nervös werden: "Kommandozeilenargumente? Klingt schwierig. Es waren doch erst 17 Jahre … könntet Ihr nicht noch ein paar Jahre länger in der Versenkung verschwinden?". Keine Angst, es gibt keinen Grund zur Sorge. Wie Sie sehen werden, ist die Handhabung von Kommandozeilenargumenten ziemlich einfach. Tatsächlich können Sie sogar jetzt schon Argumente an Ihre Scripte übergeben. Geben Sie dieses einzeilige Script in Notepad ein, und speichern Sie es als args.vbs:

Wscript.Echo strComputer

Jetzt starten Sie das Script über den folgenden Befehl im Kommandozeilenfenster:

cscript args.vbs atl-ws-01

Und? Was passiert? Na gut – nichts. Wenn Sie ein regelmäßiger Tales from the Script-Leser sind, dann sind Sie das jedoch gewohnt. Warum passiert nichts (abgesehen von einer leeren Ausgabezeile)? Ganz einfach: Der Variable strComputer wird kein Wert zugewiesen. Kümmern Sie sich nicht weiter darum – wichtig ist nur, dass Sie dem Script ein Argument übergeben haben (atl-ws-01) und Ihr Script diesen problemlos akzeptiert hat. Mit anderen Worten: Windows Script Host akzeptiert Kommandozeilenargumente, ohne dass besondere Maßnahmen erforderlich sind. Sie müssen Ihrem Script nur ein oder zwei Zeilen Code hinzuzufügen und die Arguemente auch verwenden.

Vielleicht haben Sie sich gefragt "Was ist mit dem Argument passiert? Ist er einfach verloren gegangen?". Die Antwort ist: Nein, er ist nicht verloren gegangen. Alle an ein Script übergebenen Argumente werden in der WSH-Collection Arguments gespeichert. Diese Collection wird bei jeder Ausführung eines Scripts automatisch erstellt. Es handelt sich eigentlich nicht um mehr als einen Array mit allen übergebenen Argumenten. Als Sie das Script gestartet haben, wurde das Argument atl-ws-01 in der Arguments-Collection gespeichert. Wir haben nur keinen Code in unserem Script, der diese Collection ausliest. Aber lassen Sie uns einem Moment Zeit, und wir zeigen Ihnen den entsprechenden Code.

Anmerkung: Bevor wir fortfahren, sollten wir wissen, dass mehrere Argumente beim WSH durch Leerzeichen getrennt werden. Nehmen wir einmal an, Sie geben folgenden Befehl ein:

cscript args.vbs atl-ws-01 atl-ws-02

In diesem Fall werden zwei Argumente verwendet: atl-ws-01 Und atl-ws-02. Sehen Sie sich nun diesen Befehl an:

cscript args.vbs atl-ws-01/atl-ws-02/atl-ws-03

Wie viele Argumente werden hier verwendet? Richtig – nur ein einziger (allerdings ein ziemlich langer): atl-ws-01/atl-ws-02/atl-ws-03. Einzelne Argumente müssen durch Leerzeichen getrennt werden. Der Befehl müsste also so aussehen:

cscript args.vbs atl-ws-01 atl-ws-02 atl-ws-03

Was passiert, wenn mehr als ein Leerzeichen zwischen den Argumenten ist? Zum Beispiel:

cscript args.vbs atl-ws-01 atl-ws-02   atl-ws-03

Kein Problem – der WSH verwirft überflüssige Leerzeichen automatisch. Was aber, wenn die Leerzeichen Teil des Arguments sein sollen? Zum Beispiel wenn Sie versuchen, einen Ordnernamen zu übergeben: C:\Dokumente und Einstellungen. In diesem Fall müssen Sie das Argument in Anführungszeichen einfassen. Zum Beispiel so:

cscript args.vbs atl-ws-01 "C:\Dokumente und Einstellungen"

OK, was passiert noch gleich mit den Argumenten? Sie werden wie gesagt in einem Array gespeichert. Wie bei den meisten Arrays hat auch bei diesem jedes Element eine Indexnummer. Das erste Element hat den Index 0, das zweite 1 usw. In unserem Beispielscript sieht der Array so aus:

IndexWert

0

atl-ws-01

1

atl-ws-02

2

atl-ws-03

Warum interessiert uns das? Tja, glauben Sie es oder nicht, Sie wissen jetzt, wie Sie auf die Argumente zugreifen. Sehen Sie sich dieses Script an:

strComputer = Wscript.Arguments.Item(0) 
Wscript.Echo strComputer

Geben Sie das Script in Notepad ein, speichern Sie es als args.vbs, und führen Sie es mit dem folgenden Befehl aus:

cscript args.vbs 
atl-ws-01

Wissen Sie, was passiert? Dieses Mal gibt das Script den Wert atl-ws-01 zurück. Und nicht nur dass – es gibt jeweils den Wert zurück, den Sie ihm übergeben haben. Das glauben Sie nicht? Führen Sie das Script über den folgenden Befehl aus und sehen Sie selbst:

cscript args.vbs dies_ist_mein_kommandozeilenparamter

Rufen Sie das Script mit beliebigen Argumenten auf – es funktioniert immer

Darum haben wir oben gesagt "Keine Sorge". Wenn Sie sich das Script ansehen, dann erkennen Sie, dass in der ersten Zeile der Variabel strComputer der Wert aus Wscript.Arguments.Item(0) zugewiesen wird. Worum handelt es sich bei Wscript.Arguments.Item(0)? Es handelt sich um das erste Element (das Element mit dem Index 0) aus der Collection Arguments. Mit anderen Worten, strComputer wird auf das erste Argument gesetzt, den Sie beim Scriptstart angegeben haben. Möchten Sie ein Script erstellen, das gegen einen beliebigen Computer ausgeführt werden kann? Na, dann mal los:

strComputer = Wscript.Arguments.Item(0) 
Set objWMIService = GetObject _
  ("winmgmts:\\" & strComputer & "\root\cimv2") 
Set colServices = objWMIService.ExecQuery _
  ("Select * FROM Win32_Service WHERE Name = 'Alerter'") 
For Each objService in colServices
  objService.StopService
Next

Ist Ihnen klar, wie das Script funktioniert? In den Beispielscripten aus dem Script-Center lautet die erste Zeile meist:

strComputer = "."

Mit dieser Zeile wird der Variable strComputer der Wert Punkt (.) zugewiesen. Dies passiert zum Beispiel bei allen WMI-Scripten, da in WMI ein Punkt für den lokalen Computer steht. Wegen dieser Zeile werden die meisten Scripte gegen den lokalen Computer ausgeführt.

Bei unserem veränderten Script haben wir nun eine winzige Änderung vorgenommen: Wir weisen strComputer statt dem Wert Punkt das erste an das Script übergebene Argument zu. Sie möchten das Script erneut ausführen? Gegen den Computer redmond-ws-87? Dann starten Sie es mit dem folgenden Befehl:

cscript
 args.vbs redmond-ws-87

Sie möchten das Script wieder gegen den lokalen Computer ausführen? Geben Sie einfach den Punkt als Kommandozeilenargument an:

cscript args.vbs .

Also, wenn das keine 17 Jahre Wartezeit wert war, dann wissen wir auch nicht mehr weiter.

Zum SeitenanfangZum Seitenanfang

Coole Sache – es wäre aber noch viel cooler, wenn …

Stimmt. Sie haben Recht. Wir haben jetzt ein Script, das den Warndienst auf jedem Computer im Universum beenden kann (natürlich vorausgesetzt, dass Sie lokale Administratorrechte für das Universum haben). Das ist zwar toll, aber wir würden wetten, dass Systemadministratoren nicht ihre gesamte Zeit mit dem Beenden des Warndienstes verbringen. Richtig schön wäre es, wenn das Script jeden Dienst auf jedem Computer beenden könnte. Aber ist das machbar?

Tja, wir haben zwar 17 Jahre gebraucht, aber wir haben eine Antwort auf diese Frage gefunden: Ja, es ist machbar. Bevor wir uns damit beschäftigen aber erst ein kurzes Quiz. Was, glauben Sie, macht der WSH mit den Argumenten beim folgenden Befehl?

cscript args.vbs
 atl-ws-01 cisvc

Na klar – die beiden Argumente werden einfach in die Collection übernommen. Das bedeutet, dass die Collection bei diesem Script so aussieht:

IndexWert

0

atl-ws-01

1

cisvc

Beeindruckend, was? Aber es kommt noch mehr. Vor ein paar Minuten haben wir Ihnen gezeigt, wie Sie auf das erste Argument zugreifen können.

strComputer = Wscript.Arguments.Item(0)
Wscript.Echo strComputer

Nicht sonderlich originell: Wir weisen den Wert des ersten Elements der Collection (Indexnummer 0) der Variable strComputer zu. Was glauben Sie also, wie wir an das zweite Argument kommen (Index 1)? Genau. Wir weisen dessen Wert einer zweiten Variable zu:

strComputer = Wscript.Arguments.Item(0) 
Wscript.Echo strComputer 
strService = Wscript.Arguments.Item(1) 
Wscript.Echo strService

Wenn Sie dieses Script mit dem Befehl cscript args.vbs atl-ws-01 cisvc starten, dann erhalten Sie die folgende Ausgabe:

atl-ws-01 cisvc

Fällt Ihnen ein Muster auf? Nehmen wir mal an, wir haben einem Script 199 Argumente übergeben. Wie würden Sie auf das letzte Argument zugreifen? Sie würden die folgenden Codezeilen verwenden:

strLastArgument = Wscript.Arguments.Item(198) 
Wscript.Echo strLastArgument

Warum Wscript.Arguments.Item(198)? Denken Sie daran, dass das erste Element eines Arrays die Indexnummer 0 hat, das zweite die Nummer 1 usw. Wenn Sie diese Reihe weiter verfolgen, dann sehen Sie, dass das 199 Element die Nummer 198 hat. Darum Wscript.Arguments.Item(198). Was, wenn wir auf Argument 37 zugreifen wollen? Genau: Wscript.Arguments.Item(36).

Jetzt ist es nur noch ein kleiner Schritt zu einem Script, das jeden Dienst auf jedem Computer beendet. Folgendes müssen wir noch erledigen:

Code einfügen, der den Namen des Dienstes zur Variable strService zuweist.

Die Variable strService in unserer WHERE-Bedingung verwenden.

Das Ergebnis könnte so aussehen:

strComputer = Wscript.Arguments.Item(0) 
strService = Wscript.Arguments.Item(1) 
Set objWMIService = GetObject _
  ("winmgmts:\\" & strComputer & "\root\cimv2") 
Set colServices = objWMIService.ExecQuery _
  ("Select * FROM Win32_Service WHERE Name = '" & strService & "'")
For Each objService in colServices
  objService.StopService
Next

Anmerkung: Ja, die WHERE-Bedingung sieht jetzt mit den ganzen Hochkommata, den Anführungszeichen, den &Zeichen und der eigenartigen Interpunktion ein wenig komisch aus. Wenn Sie nicht genau wissen, wie die Verkettung von Strings funktioniert, dann lesen Sie den Abschnitt "Verketten von Zeichenketten" im Microsoft Windows 2000 Scripting-Handbuch.

Was passiert, wenn wir drei Argumente verwenden wollen? Zum Beispiel möchten wir die Möglichkeit in das Script einbauen, einen Dienst beenden und starten zu können. Nun, wir könnten das Script beispielsweise so aufrufen:

cscript args.vbs atl-ws-01 cisvc start

Und das entsprechende Script könnte so aussehen:

strComputer = Wscript.Arguments.Item(0)
strService = Wscript.Arguments.Item(1)
strAction = Wscript.Arguments.Item(2) 
Set objWMIService = GetObject _
  ("winmgmts:\\" & strComputer & "\root\cimv2") 
Set colServices = objWMIService.ExecQuery _
  ("Select * FROM Win32_Service WHERE Name = '" & strService & "'") 
For Each objService in colServices
  If strAction = "start" Then
    objService.StartService
  ElseIf strAction = "stop" Then
    objService.StopService
  End If
Next

Die durchzuführende Aktion (Start) wird als drittes Argument an das Script übergeben. Daher weisen wir der Variable strAction den Wert aus Wscript.Arguments.Item(2) zu.

Das Script funktioniert auch ganz prima - aber nur so lange, bis jemand die Argumente in der falschen Reihenfolge angibt. Was passiert zum Beispiel beim folgenden Befehl?

cscript args.vbs start 
atl-ws-01 cisvc

Genau: Das Script versucht eine Verbindung mit einem Computer namens start aufzubauen – und von da ab geht das Ganze den Bach runter. Gibt es einen Weg, um solche Probleme zu vermeiden? Den gibt es tatsächlich. Und wir werden auch sofort sehen, wie das geht.

Zum SeitenanfangZum Seitenanfang

Eins ist nicht sehr viel

Kein Zweifel, Sie sind froh, einen einzelnen Computernamen an das Script übergeben zu können. Was aber, wenn Sie mehrere Computernamen an ein Script übergeben möchten? Mit anderen Worten, wenn Sie einen Befehl wie den folgenden ausführen:

cscript args.vbs atl-ws-01 atl-ws-02 atl-ws-03 atl-ws-04

Sie möchten, dass das Script gegen die folgenden vier Computer ausgeführt wird:

atl-ws-01

atl-ws-02

atl-ws-03

atl-ws-04

Ist das möglich? Darauf können Sie wetten – auch wenn der Weg, an den Sie gedacht haben, möglicherweise nicht der beste ist. Sie haben gedacht: "Ok, atl-ws-01 ist das erste Element im Array. Ich kann es also über Wscript.Arguments.Item(0) abrufen. atl-ws-02 ist das zweite Element. Ich kann es über Wscript.Arguments.Item(1) abrufen…". Wenn das Ihr Plan war, dann hören Sie auf. Das funktioniert nicht.

Gut, technisch gesehen könnte es funktionieren. Die Programmierung wäre jedoch ein Alptraum. Was, wenn jemand nur drei Computernamen angegeben hat? In diesem Fall würde das Script ohne eine ausreichende Fehlerbehandlung beim Zugriff auf Wscript.Arguments.Item(3) vor die Wand fahren. Was, wenn der Benutzer fünf Computernamen angibt? Oder 50? Oder 500? Glauben Sie uns: Der Zugriff auf die Argumente über die Indexnummern wird sich dann zu einem echten Alptraum entwickeln.

Glücklicherweise gibt es einen besseren Weg. Wir haben gesagt, dass die Argumente in der Collection Arguments gespeichert werden. Erinnern Sie sich? Keine große Sache sagen Sie? Tja, in Wirklichkeit ist es eine große Sache. Wenn Sie sich an die erste Lektion im Scripting-Einmaleins erinnern, dann wissen Sie, dass Sie mit einer For-Each-Schleife auf die Argumente der Collection zugreifen können. Schreiben Sie den folgenden Code in Notepad und speichern Sie die Datei als args.vbs:

For Each strArgument in Wscript.Arguments
Wscript.Echo strArgument Next

Jetzt führen Sie das Script über den folgenden Befehl aus:

cscript args.vbs atl-ws-01 atl-ws-02 atl-ws-03 atl-ws-04

Und? Was erhalten Sie zurück? Genau: Das Script gibt alle Kommandozeilenargumente zurück – obwohl Sie keine einzige Indexnummer angegeben haben:

atl-ws-01 atl-ws-02
 atl-ws-03 atl-ws-04

Jetzt sind wir auf der richtigen Spur. Nehmen wir an, dass Sie ein Script möchten, das den Warndienst auf einer bestimmten Zahl an Computern anhält:

For Each strArgument in Wscript.Arguments
  strComputer = strArgument 
  Set objWMIService = GetObject _
    ("winmgmts:\\" & strComputer & "\root\cimv2")
  Set colServices = objWMIService.ExecQuery _
    ("Select * FROM Win32_Service WHERE Name = 'Alerter'")
  For Each objService in colServices
    objService.StopService     
  Next
Next

Wie Sie sehen, ist das ziemlich einfach. Wir erstellen eine For-Each-Schleife, in der wir die gesamte Arguments-Collection durchlaufen. In der ersten Codezeile innerhalb der Schleife weisen wir den Wert des aktuellen Elements in der Collection zur Variable strComputer zu. Diese wird dann verwendet, um eine Verbindung mit dem entsprechenden Computer aufzubauen. Wenn wir das Script ausführen, dann weist das Script das erste Argument- atl-ws-01- der Variable strComputer zu. Dann baut das Script eine Verbindung mit dem Computer auf, beendet den Warndienst und beginnt wieder am Anfang der Schleife (um zu sehen, ob noch ein weiteres Element in der Collection vorhanden ist).

Wie sich herausstellt, gibt es weitere Argumente. Daher nimmt das Script das zweite Argument - atl-ws-02 - und weist diesen der Variable strComputer zu. Dieser Vorgang wir so lange wiederholt, bis alle Argumente abgearbeitet sind. In diesem Moment wird die Schleife beendet, und das Script ist abgeschlossen.

Sehr einfach – und jetzt ist es auch kein Problem mehr, das Script gegen eine fast beliebige Anzahl von Computern auszuführen.

Zum SeitenanfangZum Seitenanfang

Wie sieht es mit beliebig vielen Diensten auf beliebig vielen Computern aus?

Wir haben schon befürchtet, dass jemand diese Frage stellen würde. Der Leser mit dem roten T-Shirt dort hinten möchte wissen, wie wir ein Script erstellen könnten, das jeden beliebigen Dienst auf einer beliebigen Anzahl von Computern anhält. Können wir das? Hey, wir sind die Scripting Guys – wir können alles (nun gut, zumindest dann, wenn wir 17 Jahre Zeit zum Überlegen haben). Ein solches Script ist allerdings ein wenig komplizierter.

Hierzu müssen wir Ihnen als erstes ein Geständnis machen: Wir haben Ihnen etwas vorenthalten. Wir haben Ihnen erzahlt, dass der WSH eine Collection mit dem Namen Arguments hat – und das stimmt auch (schauen Sie doch nach, wenn Sie uns nicht glauben). Es gibt aber noch zwei weitere Argument Collections, die wir unterschlagen haben: Named und Unnamed. Wenn das Script etwas komplexeres machen soll, zum Beispiel das Beenden von beliebigen Diensten auf einer beliebigen Anzahl von Computern, dann ist der einfachste Weg hierzu die Verwendung der beiden bisher nicht erwähnten Collections.

Was sind also Named- und Unnamed-Argumente? Sehen Sie sich den folgenden Befehl an:

cscript args.vbs /service:alerter atl-ws-01

Es gibt zwei Argumente: /service:alerter und atl-ws-01. Wenn Sie die Arguments-Collection durchgehen, dann sehen Sie zwei Elemente: /service:alerter und atl-ws-01.

Interessant ist das Format des ersten Arguments: /service:alerter. Warum haben wir das Argument auf die Art angegeben? Tja, mit dem WSH sind sogenannte Named-Argumente (benannte Argumente) möglich. Diese verwenden das Format /Argumentname:Argumentwert. Mit anderen Worten: Wir verwenden ein Argument mit dem Namen service und dem Wert alerter.

Kann uns das nicht egal sein? Nein – sehen Sie sich den folgenden Scriptcode an:

Wscript.Echo Wscript.Arguments.Named("service")

Was passiert hier? Tja, wir geben einfach den Wert des Arguments mit dem Namen service aus. So erhalten wir den Wert alerter. Alles klar? Nehmen wir an, Sie starten das Script mit dem folgenden Befehl:

cscript args.vbs /service:cisvc atl-ws-01

Was würden wir zurückbekommen? Genau: cisvc. Das liegt daran, dass cisvc der Wert des Arguments mit dem Namen service ist.

Verwirrt? Es ist wirklich ganz einfach – nehmen wir an, ein Script wird mit dem folgenden Befehl gestartet:

cscript args.vbs /service:cisvc /computer:atl-ws-01

Die Collection Arguments.Named sähe dann so aus:

NameWert

service

cisvc

computer

atl-ws01

Mit dem folgenden Code können wir auf diese Argumente zugreifen:

Wscript.Echo Wscript.Arguments.Named("service") 
Wscript.Echo Wscript.Arguments.Named("computer")
Zum SeitenanfangZum Seitenanfang

Was ist aber nun aus dieser "jeder Dienst auf jedem Computer"-Geschichte geworden?

Haben Sie Geduld. Wie werden die Named- und Unnamed-Argumente im folgenden Befehl verwendet?

cscript args.vbs /service:alerter atl-ws-01

Das Argument service abzufragen ist ganz einfach:

Wscript.Echo Wscript.Arguments.Named("service")

Aber wie kommen wir an den Computernamen am Ende?

Das ist viel einfacher, als Sie möglicherweise denken. Verwenden Sie einfach die Collection Arguments.Unnamed (atl-ws-01 ist schließlich ein unbenanntes Argument ohne Name). Hätten wir einen Namen verwenden können? Klar – wir könnten den folgenden Befehl verwenden:

cscript
 args.vbs /service:alerter /computer:atl-ws-01

Das Beenden eines beliebigen Dienstes auf einem beliebigen einzelnen Computer ist also ganz einfach. Wir wollten aber einen bestimmten Dienst auf einer Gruppe von Computern beenden. Was wir wirklich brauchen, ist die Möglichkeit, einen solchen Befehl zu verwenden:

cscript args.vbs /service:alerter atl-ws-01 atl-ws-02 atl-ws-03 atl-ws-04

Aber wie greifen wir auf diese vier Computernamen zu? Der einfachste Weg ist wiederum die Verwendung der Unnamed-Collection. Geben Sie beispielsweise den folgenden Code ein, und speichern Sie ihn als args.vbs:

For Each objArgument in Wscript.Arguments.Unnamed  
  Wscript.Echo objArgument 
Next

Jetzt starten Sie das Script mit dem folgenden Befehl:

cscript
 args.vbs /service:alerter atl-ws-01 atl-ws-02 atl-ws-03 atl-ws-04

Was passiert? Genau: Alle vier Computernamen – die vier Unnamed-Argumente – werden zurückgegeben. Warum wird das Argument service nicht zurückgegeben? Ganz klar: Es handelt sich um ein Named-Argument. Das bedeutet, dass er nicht in der Collection Arguments.Unnamed enthalten ist (wenn Sie die Collection Wscript.Arguments.Named durchgehen, dann wird auch das Argument service angezeigt.)

Nur um sicherzugehen, dass das Ganze auch jeder verstanden hat, wollen wir uns die verschiedenen Arguments-Collections noch einmal anschauen. Als erstes die allgemeine Arguments-Collection:

Arguments-Collection

/service:alerter

atl-ws-01

atl-ws-02

atl-ws-03

atl-ws-04

Alles klar? OK, die Named-Collection sieht so aus:

Arguments.Named-CollectionValue

service

alerter

Beachten Sie, dass ein benanntes Argument aus zwei Teilen besteht: aus dem Namen und dem Wert.

Und als letztes die Arguments.Unnamed-Collection:

Arguments.Unnamed-Collection

atl-ws-01

atl-ws-02

atl-ws-03

atl-ws-04

Glauben Sie es oder nicht, aber das war alles. Geben Sie das folgende Script in Notepad ein, und speichern Sie es als args.vbs:

Wscript.Echo 
Wscript.Arguments.Named("service") 
For Each objArgument in Wscript.Arguments.Unnamed 
  Wscript.Echo objArgument 
Next

Nun starten Sie das Script mit dem folgenden Befehl:

cscript args.vbs /service:alerter atl-ws-01 atl-ws-02 atl-ws-03 atl-ws-04

Überraschung! Das Script gibt erst den Wert des Named-Arguments service aus und dann alle vier Unnamed-Argumente. Unsere Frage "Kann ich ein Script schreiben, das einen beliebigen Dienst auf mehreren Computern beendet" ist damit beantwortet. Ja, Sie können:

strService = Wscript.Arguments.Named("service") For Each strArgument in Wscript.Arguments.Unnamed strComputer = strArgument Set objWMIService = GetObject _ ("winmgmts:\\" & strComputer & "\root\cimv2") Set colServices = objWMIService.ExecQuery _ ("Select * FROM Win32_Service WHERE " & _ "Name = '"& strService & "'") For Each objService in colServices objService.StopService Next Next

Wiederum eigentlich ganz einfach. Als erstes holen wir uns den Wert des Named-Arguments service und speichern diesen in der Variable strService. Dann erstellen wir eine For-Each-Schleife, die alle Unnamed-Argumente durchläuft (in diesem Fall alle Computer, gegen die wir das Script ausführen wollen). In jedem Schleifendurchlauf holen wir uns den Computernamen, weisen diesen der Variable strComputer zu und führen den Hauptteil des Scripts aus. Diese macht folgendes:

Verbindung mit dem Computer aufbauen, dessen Name in der Variable strComputer steht.

Den über strService angegebenen Dienst beenden.

Named-Argumente beheben auch das Problem durch unterschiedliche Argumentreihenfolgen. Wenn ein Benutzer also zum Beispiel den folgenden Befehl eingibt

cscript args.vbs
 /service:alerter /computer:atl-ws-01

dann kann es durchaus sein, dass ein zweiter Benutzer dasselbe Script mit den folgenden Befehl startet:

cscript args.vbs /computer:atl-ws-01 /service:alerter

Das macht jedoch keinen Unterschied. Warum? Erinnern Sie sich – bei der Arbeit mit Named-Argumenten verwenden wir den Argumentnamen und nicht die Indexnummer. Wir suchen also nach einem Argument mit dem Namen computer und nach einem mit dem Namen service. Es ist vollkommen egal, in welcher Reihenfolge diese übergeben wurden. Bei Named-Argumenten ist die Indexnummer egal. Dies gilt jedoch nicht zwingend auch für alle Unnamed-Argumente.

Zum SeitenanfangZum Seitenanfang

Wie geht es weiter?

Ist das alles, was Sie mit Kommandozeilenargumenten machen können? Nein, nicht mal annährend. Es gibt zum Beispiel Möglichkeiten, mit denen Sie prüfen können, ob bestimmte Argumente angegeben wurden. Andernfalls können Sie den Benutzer zu deren Eingabe auffordern. (Andererseits: Was wäre das für ein brilliantes Script, dass einen Dienst beenden kann, obwohl der Benutzer die Angabe des zu beendenden Dienstes vergessen hat?) Wir haben heute jedoch keine Zeit, uns diese Features genauer anzusehen. Sie können aber in Kapitel 3 ("Der WSH") im Microsoft Windows 2000 Scripting-Handbuch weitere Informationen zu diesem Thema finden.

Wir werden uns in der Zwischenzeit an die Kolumne für den nächsten Monat machen – denn wahrscheinlich schreiben wir das Jahr 2021 schneller als uns lieb ist ...


Zum SeitenanfangZum Seitenanfang