http://dieseyer.de • all rights reserved • © 2003 v3.7



CDdurchsuchen.vbs - Skript erstellen


Einleitung

Speziell für Umsteiger, Einsteiger und Anfänger ist dieser Text gedacht, in dem an einem Beispiel jeder Schritt erklärt wird, wie ich ein neues Skript schreibe. Vorausgehend sollte dse-wsh-lernen.html gelesen werden. Wie dort beschrieben, wird zunächst ein Skript kopiert und umbenannt, das als Ausgangsbasis dienen soll.

Die Überschriften sind Links und verweisen auf eine Datei, die die Veränderungen zeigt, die nach der jeweiligen Überschrift erklärt werden. In dem Skript sind gelöschte Zeilen auskommentiert (mit einem Hochkomma) und rot dargestellt; Änderungen sind blau und eingefügte Zeichen oder Zeilen grün. Markiert man alle Zeilen, kopiert diese und fügt sie in eine .VBS-Datei ein (Copy & Paste), erhält man ein lauffähiges Skript.


Die Aufgabe

Dazu möchte ich auf eine E-Mail verweisen, die ich Anfang Juli 2003 erhielt (etwas gekürzt):


Hallo Dieseyer,
meine Vorgehensweise ist so zwar wohl nicht vorgesehen, andererseits
offensichtlich aber auch nicht ganz ausgeschlossen.
Nachstehend 2 Batches, die ich jetzt gerne umstellen möchte. Kannst
Du mir da bitte mal Hilfestellung geben?
. . . fällt es schon schwer, mal was ganz Neues anzufangen. Aber mit
nachvollziehbarer Hilfestellung wird es mir dann schon möglich sein,
andere Aufgaben umzustellen oder neu zu schreiben.
Ich habe mich auch durch das empfohlene Forum gehangelt, habe da aber
nichts entsprechendes gefunden.
Ansonsten, ... , nichts für ungut und entschuldige bitte.

Vor etwas mehr als einem Jahr (Anfang 2002) bin ich von .BAT zu .VBS umgestiegen. Mein größtes Problem war das, was als objektorientiertes Programmieren (OOP) bezeichnet wird. Andererseits ist es auch nicht selten, dass ich auf Befehlszeilenkommandos (dir, attrib, ping . . .; unter WinNT/2k/XP gibt's keine DOS-Kommandos, das sind dann Befehlszeilenkommandos) zurückgreife, weil diese eben viel schneller sind (und ich weiß, wie die funktionieren). Die jungen (Skript-Kiddis) müssen (oder wollen) ein 'DIR /S' (Vergl. dateinamenlangdir.vbs) unbedingt als Skript nach bauen (und finden das dann einfach viel zu langsam).

Die erste der beiden .BAT-Dateien, die ich mit dieser E-Mail erhalten habe, wird mit dem Laufwerksbuchstaben des CD-Laufwerks gestartet. 'Dir /s > cd.txt' schreibt dann eine Dateiliste in die Datei CD.TXT. Die zweite .BAT-Datei muss mit zwei Parametern gestartet werden: 'bat2 XYZ cd.txt', um in der Textdatei nach 'XYZ' zu suchen.

Über den Sinn der beiden .BAT-Dateien und ob es lohnendere Aufgaben gibt, verliere ich kein Wort. Denn im folgenden soll das ganze nur 'zeitgemäßer' in einem Skript nachgebildet werden.


Vorüberlegungen

Als Ausgangspunkt für das Skript habe ich mir überlegt, dass man beides in ein Skript packen sollte. Ruft man das Skript mit einem Doppelklick auf, sucht es das CD-Laufwerk und schreibt die Inhaltsliste in eine Datei, die dann in dem Verzeichnis liegt, in dem sich das Skript befindet. Zieht man eine Inhaltsliste auf das Skript, öffnet sich ein Fenster, in dem man den Suchbegriff eingeben kann.

Da möglicherweise Inhaltslisten verschiedener CD's abgespeichert werden sollen, muss man sich was für den Dateinamen einfallen lassen. Fortlaufend nummerieren oder Datum und Uhrzeit halte ich für wenig geeignet, da man (per Hand) eine Liste führen müsste, um die Inhaltslisten den jeweiligen CD's zuordnen zu können. Es muss also ein Weg gesucht werden, wie man den im Explorer angezeigten Namen (Label) verwenden kann.

In den ersten Zeilen des Skripts muss also der Laufwerksbuchstabe des CD-Laufwerkes eingetragen sein und der übergebene Parameter (wenn man eine Datei auf das Skript zieht) erfasst werden.

Andererseits, um das Skript so universell wie möglich zu gestalten, sollte es selbst das (erste) CD-Laufwerk finden. Und da selbst gebrannte CD's mitunter immer wieder den selben Namen (Label) erhalten, ist eine fortlaufende Nummerierung (der gleichen Namen) erforderlich.


CDdurchsuchen0 - Skriptauswahl

Wenn ich nun die Liste meiner alten Skripte durchgehe, entscheide ich mich für LaufWerkListe.vbs als Ausgangsbasis. Dieses Skript kopiere ich in mein Arbeitsverzeichnis und nenne es in CDdurchsuchen.vbs um.

Neben einer Versionsnummer ist im ersten Block der Skriptname auszutauschen und anschließend eine kurze Skriptbeschreibung zu verfassen.

Welche Zeilen des alten Skripts werden auch weiterhin benötigt und welche sind zu löschen?

Mit Option Explicit ist festgelegt, dass nur Variable im Skript benutzt werden, die mit DIM-Anweisungen deklariert sind. Vertippt man sich bei der Eingabe von Variablen, wird das mit einer Fehlermeldung angezeigt. (Würde man versehentlich MsgBox Txet statt MsgBox Text schreiben, würde ohne Option Explicit eine leere MsgBox erscheinen.)

Zur Vereinfachung des Zugriffs auf Objekte werden mit Set = einigen Variablen Verweise auf Objekte zugewiesen: Objektverweise. Für ein Popup bedeutet das statt
      WScript.CreateObject("WScript.Shell").Popup "Meldung"
nach dem Objektverweis auf die Variable 'WS'
      Set WS = WScript.CreateObject("WScript.Shell")
nur noch:
      WS.Popup "Meldung"
Da WS auch auf WScript verweisen könnte, verwende ich WSHShell; andere Programmierer nehmen lieber oWSHShell als Variable.

Eine Protokolldatei ist unnötig => 3 Zeilen löschen.
Von den möglichen Laufwerkstypen interessiert nur das CD-Laufwerk => 5 Zeilen löschen.
Text1 und Text3 scheint auch nicht sonderlich wichtig => 8 Zeilen löschen.
Da zu jedem If .. ein End If und zu jedem For .. ein Next gehört, bleiben die beiden Zeilen stehen; die beiden dazwischen und die folgende => 3 Zeilen löschen.

Das Skript speichern und ausführen - auf Grund der letzten Zeile dürfte nur 'Text2' als Meldung zu sehen sein.

Alle Variablen bzw. deren Namen sind frei erfunden . . . aber die Ähnlichkeit mit realen Dingen ist nicht rein zufällig, sondern beabsichtigt. Um das Skript lesbar zu gestalten, empfehlen sich sinnvolle Variablen. Hat man ein Skript einige Zeit nicht mehr verwendet und will es dann doch noch einmal ändern, erleichtert dies die Einarbeitung. Um auch anderen den Zugang zu eigenen Skripten zu erleichtern, habe ich mir angewöhnt, die Variablen aus der 'Windows Script'-Hilfe zu übernehmen.

Im Skript verweist die Variable fso durch Set fso auf das FileSystemObject. Über das FileSystemObject lassen sich verschiedene Eigenschaften abrufen und Dateien und Verzeichnisse manipulieren. Unter anderem kann über die Eigenschaft .Drives auf die Liste der (verbundenen) Laufwerke zugegriffen werden.

Auf welche Eigenschaften und Methoden über das FileSystemObject zugegriffen werden kann, läßt sich über die Hilfe herausfinden. Da ich immer wieder verwechsle, ob die Existenz einer Datei oder eines Verzeichnis über FileExists oder ExistsFile getestet wird, suche ich in der Hilfe garantiert das erste mal mit dem falschen Suchbegriff. Um dies zu umgehen, habe ich den FileSystemObject-Beispielcode der Hilfe (fso-beispielcode.vbs) in meine Skriptsammlung aufgenommen und suche dort. Auch eine Suche nach 'ready' ist dann erfolgreich, wenn mir entfallen ist, dass es eigentlich 'IsReady' heißen müsste. Um dem geneigten Leser auch für diesen Text die Möglichkeit einer Volltextsuche zu geben, ist dieser komplette Text in nur einer Datei (mit über 40KB für das Internet eigentlich zu groß) gespeichert.

Hier könnte eine Vereinfachung durchgeführt werden, da die .Drives-Eigenschaft nur einmal erforderlich ist.
      Set fso = CreateObject("Scripting.FileSystemObject")
      Set DriveList = fso.Drives
läßt sich zusammenfassen:
      Set DriveList = CreateObject("Scripting.FileSystemObject").Drives
Da das fso noch für das Einlesen und Durchsuchen der Inhaltsliste nötig ist, bleibt alles es unverändert.

Also: DriveList verweist auf die Drives-Eigenschaft des FileSystemObject. Um jedes Laufwerk zu testen, ob es ein CD-Laufwerk ist, muss für jedes (For .. Each i) Laufwerk der Laufwerksliste (DriveList) ein Test gemacht werden. Diese Form der variablen For .. Next-Schleifen mit unbekanntem Ende (je nach dem, wie viele Laufwerke auf einem PC zur Verfügung stehen), werden in 'Windows Script' häufig verwendet.

Die Variable i enthält aber keinen Zähler (1, 2, 3, ...) sondern einen (Objekt-) Verweis auf ein Laufwerk (Laufwerksbuchstabe; z.B. C:). Über die Eigenschaft .DriveType des jeweiligen Laufwerkes läßt sich ermitteln, ob es sich um ein CD-Laufwerk handelt und mit .IsReady ob es bereit, also eine CD eingelegt ist.


CDdurchsuchen1 - CD-Laufwerke finden

Die folgenden zwei Zeilen werden zusammen gefasst (und in der ersten der beiden Zeilen einiges gelöscht):
      if 4 = i.DriveType Then Text1 = "CD-Lw. " & vbTab & i.DriveLetter & ": " & vbTab
      if If i.IsReady Then
In der entstandenen If-Anweisung müssen beide Fragen 4=i.DriveType und i.IsReady mit 'richtig' (True) beantwortet werden, damit die Folgezeile abgearbeitet wird; sonst geht es erst bei End If.
      if 4 = i.DriveType AND i.IsReady Then

Eigentlich hätte die Zeile sogar so aussehen müssen:
      if 4 = i.DriveType AND True = i.IsReady Then
Wenn man richtig
      if True = i.IsReady Then
liest, bedeutet das: Wenn i.IsReady gleich True ist, ist der Vergleich True (und die If-Bedingung erfüllt).

An dieser Stelle der Skriptentwicklung fällt mir ein, dass im Explorer bei eingelegter CD der CD-Name angezeigt wird; ähnlich dem Label, das ich einer Festplatte oder Diskette beim Formatieren geben kann. Also gebe ich in der 'Windows Script'-Hilfe und gebe als Suchbegriff Label ein und finde . . . nichts. Da das nicht sein kann, wäre ein anderer Suchbegriff sinnvoll - aber welcher?

Ich suche eine Information über das Laufwerk bzw. dessen Inhalt. Eigentlich müsste es, ähnlich wie i.IsReady auch i.Label (oder so) geben. Also suche ich in der Hilfe mal nach 'IsReady', vielleicht steht in der Nähe dann das, was ich brauche. 'IsReady' wird natürlich sofort in der Hilfe gefunden und unter 'siehe auch' steht 'VolumeName' als weitere Eigenschaft (statt wie dem von mir erwarteten 'Label'). MsgBox i.VolumeName im Skript soll diese Information anzeigen.


CDdurchsuchen2 - Text 'zusammen bauen'

Mittlerweile gibt es immer mehr Computer, die mehr als ein CD-Laufwerk eingebaut haben. Man könnte von jedem CD-Laufwerk eine Inhaltsliste erstellen und anschließend über eine Dateiauswahl die 'richtige' auswählen. Der Nachteil: Das Erstellen einer Inhaltsliste kann recht lange dauern und sollte deshalb nur erfolgen, wenn die Liste auch wirklich benötigt wird. Also muss erst eine Laufwerksauswahl erfolgen.

Alle CD-Laufwerke (die bereit sind bzw. in die eine CD eingelegt ist) werden aufgelistet und der Benutzer gibt als Auswahl einen Laufwerksbuchstaben ein. Dazu wird eine Variable CDlw eingefügt, die die Informationen der CD-Laufwerke sammelt:
      CDlw = CDlw & vbTab & i.DriveLetter & vbTab & i.VolumeName & vbCRLF
Die .DriveLetter-Eigenschaft findet man, wie weiter ober die .VolumeName-Eigenschaft zu finden war.

Im einzelnen: Die Variable CDlw wird aus ihrem bisherigen Inhalt und ( & ) weiteren Informationen zusammengesetzt. Da die Variable CDlw neu im Skript ist, muss eine Dim -Zeile erweitert werden (Komma nicht vergessen!). Um den gesammelten Text etwas zu formatieren, werden die Informationen durch jeweils ein Tabulator-Zeichen ( vbTab ) auf Abstand zueinander gehalten. Bei jedem Durchlauf der For .. Each- Schleife wird die Variable CDlw um die Informationen eines weiteren CD-Laufwerkes erweitert. Damit diese nicht alle in einer Zeile stehen, wird bei jedem Durchlauf die Zeile durch einen Zeilenumbruch ( vbCRLF ) unterbrochen.

Zeilenumbruch: Das kommt noch aus der Zeit der Schreibmaschinen. Durch einen Hebel (nach rechts bewegen) wurde der Wagen, in dem das Papier transportiert wird, so bewegt, dass am Zeilenanfang (links auf dem Papier) ein Zeichen angeschlagen werden kann: Wagenrücklauf = CarrigeReturn = CR. Sobald der Hebel über den Anschlag hinaus bewegt wurde, drehte sich die Walze mit dem Papier (einzeilig, anderthalbzeilig, zweizeilig; je nach Einstellung) weiter: Zeilenvorschub = LineFeed = LF. IBM hat früher sein Geld u.a. mit Schreibmaschinen verdient und diesen Zeilenumbruch als CRLF in die Computer übernommen. (Nähere Informationen zu dieser und weiteren Konstanten sind in der 'Windows Script'-Hilfe mit dem Suchwort 'Zeichenfolgenkonstanten' zu finden.)

Natürlich soll der Inhalt der Variable CDlw auch angezeigt werden. Aus diesem Grund wird aus       MsgBox "Text2", , WScript.ScriptName die Zeile       MsgBox CDlw, , WScript.ScriptName

Wie aus der Hilfe zu entnehmen ist, können einer MsgBox verschiedene Parameter übergeben werden. Vor dem ersten Komma sollte ASCII-Text, der mit je einem Anführungszeichen beginnt und endet, oder eine Variable (mit Text) stehen. Als zweiter Parameter (nach dem ersten Komma) kann man die Box mit einem Symbol etwas gestalten und entscheiden, welche Buttons angezeigt werden. Nach dem zweiten Komma lässt sich der Titel der Box (Text im oberen blauen Balken) bestimmen. Ich verwende hier immer den Skriptnamen, der von der Eigenschaft .ScriptName des WScript-Objekts zurück gegeben wird.

Beim Ausführen des Skripts ist festzustellen, dass bei dem Laufwerksbuchstaben der Doppelpunkt fehlt.


CDdurchsuchen3 - InputBox

Zunächst wird die Zeile
      CDlw = CDlw & vbTab & i.DriveLetter & vbTab & i.VolumeName & vbCRLF
um einen Doppelpunkt für den Laufwerksbuchstaben erweitert:
      CDlw = CDlw & ":" & vbTab & i.DriveLetter & vbTab & i.VolumeName & vbCRLF

Um das Skript möglichst universell zu gestalten, sollte es auch auf Computern, die z.B. 14 (SCSI-) CD-Laufwerke zur Verfügung stellen, funktionieren. Für eine Auswahl müssen entsprechende Benutzereingaben vorgenommen werden können. In 'Windows Script' bieten sich dafür MsgBox, PopUp und InputBox an, wobei die ersten beiden nur klickbare Buttons haben. Mit InputBox lassen sich ganze Zeichenketten an ein Skript übergeben.

Der von der InputBox angezeigte Text soll noch etwas 'verfeinert' werden. Vor der Anzeige der CD-Laufwerke wird die Variable CDlw mit diesem Text vor der For .. Each-Schleife 'vorbelegt':
      CDlw = "Die CD-Laufwerke enthalten folgende CD's:" & vbCRLF & vbCRLF
Will man diese Zeile hinter die For .. Each-Schleife schreiben, würde der gesammelte Inhalt von CDlw verloren gehen - daher:
      CDlw = "Die CD-Laufwerke enthalten folgende CD's:" & vbCRLF & vbCRLF & CDlw

Die Variable CDlw muss nun noch (durch eine weitere Zeile) um eine Frage erweitert werden:
      CDlw = CDlw & "Von welchem Laufwerk soll eine Inhaltsliste erzeugt werden?"

Entsprechend der 'Windows Script'-Hilfe sieht die InputBox-Zeile dann so aus:
      CDlw = InputBox( CDlw, WScript.ScriptName)

Da der Inhalt der Variable CDlw mit ihrem Inhalt nicht mehr benötigt wird, habe ich mich entschieden, diese Variable für das Ergebnis der InputBox zu verwenden.

Wird das Skript jetzt ausgeführt, sieht man, dass es neben dem [OK]-Button einen [Abbrechen]-Button gibt. Wird dieser betätigt, ist die folgende MsgBox-Anzeige leer - was auch zu erwarten war. Da das Skript nicht 'weiter zu arbeiten' braucht, erfolgt (nach einem Test) der Skriptabbruch:
      If CDlw = "" then
          MsgBox ". . . das ist das Ende!", , Wscript.ScriptName
          Wscript.Quit
      End If

Oder alles in einer Zeile:
      If CDlw = "" then MsgBox ". . . das ist das Ende!", , Wscript.ScriptName : Wscript.Quit
Durch einen Doppelpunkt lassen sich mehrere Befehle hintereinander in eine Zeile schreiben; gleichzeitig geht aber häufig die Lesbarkeit des Skripts zurück. Hier, in diesem speziellen Fall, wird durch die ersten Zeichen der Zeile If CDlw = "" then MsgBox . . .  klar, dass es sich nur um einen Skriptabbruch bei falscher Eingabe handeln kann. Das Skript wird durch diese Zusammenfassung sogar besser lesbar (finde ich).


CDdurchsuchen4 - Eingabe auswerten

Damit das Skript einfach bleibt, soll es nicht möglich sein, mehrere Laufwerke auf einmal einzugeben. Damit braucht von der Eingabe nur das erste Zeichen ausgewertet zu werden:
      CDlw = Left( CDlw, 1)

Um Fehleingaben auszuschließen, soll jetzt geprüft werden, ob sich der eingegebene Laufwerksbuchstabe auf ein CD-Laufwerk bezieht und ob dieses bereit ist. Vielleicht ist es aufgefallen: die obige For .. Each- Schleife braucht einige Zeit, bis sie komplett durchgelaufen ist. Aus diesem Grund soll jetzt nur auf ein Laufwerk getestet werden. Dazu scheint sich der Beispiel-VBScript-Code in der 'Windows Script'-Hilfe zu der .IsReady-Eigenschaft zu eignen.

In diesem Beispiel gibt es ein 'l.IsReady', wobei der Objektverweis 'l' weiter oben mit 'Set l = fso.GetDrive(Laufwerksangabe)' festgelegt wurde. Demnach schreibe ich in mein Skript statt 'l =' besser 'i ='
      i = fso.GetDrive( CDlw )
So wird auch gleichzeitig der alte Objektverweis (weiter oben) beendet. Und:
      if 4 = i.DriveType AND i.IsReady Then
sorgt für den Test.

Im Skript ist nach der MsgBox noch ein End If einzufügen.

Wer das Skript jetzt so mitgeschrieben hat, wird eine Fehlermeldung erhalten, weil in der Zeile
      i = fso.GetDrive( CDlw )
das 'Set' fehlt. So muss das Skript aussehen:
      Set i = fso.GetDrive( CDlw )


CDdurchsuchen5 - Inhaltsliste-Name

Besser ist es, wenn bei fehlgeschlagener Überprüfung (ob die Benutzereingabe auf ein CD-Laufwerk verweist oder nicht) die Skriptabarbeitung beendet wird. Also
      if not 4 = i.DriveType OR not i.IsReady Then
statt
      if       4 = i.DriveType   AND   i.IsReady Then
Man beachte, dass beide Abfragen negiert (durch not) werden müssen und die Verknüpfung der beiden Abfragen von AND in OR zu ändern ist.

So! Nach dem klar ist, von welchem Laufwerk eine Inhaltsliste erstellt werden soll, kann nun eine Liste erstellt werden. Dazu sind noch die Fragen zu klären "Wo sollen die Listen gespeichert werden?" und "Wie sollen die Listen heißen?"

Am sinnvollsten scheint mir, die Listen in dem Verzeichnis abzulegen, in dem sich das Skript befindet. Schließlich soll es später auch die Funktion 'Inhaltsliste mit der Maus auf das Skript ziehen und fallen lassen' geben; da ist es besser, wenn das Skript im selben Verzeichnis wie die Dateien liegt. Der Dateiname sollte aus dem .VolumeName abgeleitet werden. 'Abgeleitet' deshalb, weil bei gleichem Namen (oder wiederholtem Einlesen) ein abweichender Name gefunden werden muss: dazu bietet sich die fortlaufende Nummerierung an.

Mit i.VolumeName ist der Listenname ermittelt. Den (Datei-) Type bzw. die (Datei-) Endung soll flexibler durch ein Variable LstType am Skriptanfang definiert werden.
      Liste = i.VolumeName
      LstType = ".txt"
Da diese Variablen neu im Skript sind, muss wieder am Skriptanfang 'etwas' mit Dim geschehen. Die Variable LstType wird auch gleich am Skriptanfang belegt.

Für die Nummerierung der Inhaltslisten sollten die Zahlen von 1 bis 9 genügen.

Die folgenden Skriptzeilen werden etwa so aussehen:
      if fso.FileExists( Liste & "1" & LstType ) then
        ' mehrere Zeilen
      Else
        ' eine Zeile
      End If
Dreht man das Ganze um (negieren), wird der Code wesentlich besser lesbar, weil auf einen Blick beide Verzweigungen zu sehen sind:
      if not fso.FileExists( Liste & "1" & LstType ) then
        ' eine Zeile
      Else
        ' mehrere Zeilen
      End If

Wenn es eine Liste 1 gibt:
      if fso.FileExists( Liste & "1" & LstType ) then
soll überprüft werden, ob es noch weitere Listen gibt. Andernfalls ( nach Else ) ergibt sich der Name der zu erzeugenden Inhaltsliste:
        Liste = Liste & "1" & LstType

Gibt es eine Liste 1, wird überprüft, ob es weitere gibt, die dann angezeigt werden sollen - dafür wird die Variable Text verwendet:
        For i = 1 to 9
            if fso.FileExists( Liste & i & LstType ) then
                Text = Text & Liste & i & LstType & vbCRLF
            End If
        Next

      if fso.FileExists( " . . . " ) then
ermittelt über die Eigenschaft FileExists des Objektverweis fso., ob es die spezifizierte Datei gibt.

Zur Verbesserung des Skript könnte man noch zu jeder Datei den Zeitpunkt der letzten Änderung anzeigen - dieser Zusatzaufgabe möchte ich mich hier aber nicht stellen.

Nach dem Durchlaufen der For .. Next-Schleife sind die Namen aller vorhandenen Inhaltslisten in Text gesammelt. Abschließend werden noch ein paar Erklärungen in Text eingefügt. Diesmal sollen/müssen weitere Parameter bei der Anzeige der MsgBox übergeben werden: Die Buttons [Ja], [Nein] und [Abbrechen]. In der 'Windows Script'-Hilfe ist zu erfahren, dass folgende zwei Varianten möglich sind und dieselben Anzeigen ergeben:
      MsgBox Text, 3 + 32
und
      MsgBox Text, vbYesNoCancel + vbQuestion


CDdurchsuchen6 - MsgBox-Buttons-Auswertung

Um diesen Bereich des Skripts testen zu können, sind einige Dateien anzulegen. Heißt (.VolumeName) die eingelegte CD z.B. 'Neu', sollte man mindestens zwei Dateien (neu1.txt und neu2.txt) anlegen.

Es folgt die Auswertung des betätigten Buttons:
      if Text = vbCancel then ...
      if Text = vbNo then ...
      if Text = vbYes then ...
Bei vbCancel erfolgt (wie oben) eine kurze Meldung und die Skriptabarbeitung endet. Bei vbNo sind einige Dateien zu löschen und wie bei vbYes der Name der neuen Inhaltsliste-Datei zu bestimmen.

Das Löschen ist schnell programmiert:
      if Text = vbCancel then
        For i = 1 to 9
            if fso.FileExists( Liste & i & LstType ) then
    '           fso.DeleteFile( Liste & i & LstType )
            End If
        Next
oder mit Löschen erzwingen (true):
      if Text = vbCancel then
        For i = 1 to 9
    '       if fso.FileExists( Liste & i & LstType ) then fso.DeleteFile( Liste & i & LstType ), true
        Next
Die Löschen-Zeile habe ich nach einem erfolgreichen Test auskommentiert, damit die (beiden) Testdateien nicht immer wieder gelöscht werden.

Da es keine Inhaltslisten mit ähnlichen Namen wie die zu prüfende CD mehr gibt, erhält die zu erstellende List die Nummer eins:
        Liste = Liste & "1" & LstType

Wurde der [Yes]-Button betätigt, ist der erste mögliche Name (bzw. Nummer) zu ermitteln. Das geht am einfachsten, wenn beim Testen auf vorhandene Dateien rückwärts gezählt wird und sich das Skript die Zahl merkt, für die es (noch) keine entsprechende Listendatei gibt. Die Schleife wird von neun bis eins herunter gezählt. Nur wenn es einen Dateinamen nicht gibt, wird Text = i (neu) gesetzt:
      if Text = vbYes then
          For i = 9 to 1 Step -1
              if not fso.FileExists( Liste & i & LstType ) then Text = i
          Next
Sollte nach dem Durchlaufen dieser Schleife Text immer noch vbYes enthalten, ist Text kleiner als 'eins' - das heißt, es gibt neun Dateien (und eine Fehlermeldung). Andernfalls enthält Text die Zahl, aus der ein Dateiname zusammengesetzt wird:
      Liste = Liste & Text & LstType


CDdurchsuchen7 - Inhaltsliste erstellen

Der Inhaltslistenname steht nun fest und man kann sich Gedanken machen, was in der Liste alles stehen soll. Als Kopfzeile genügt empfehle ich Name und Erstellungzeitpunkt der Liste.

Um in eine Datei etwas schreiben zu können, muss diese angelegt werden. (Mir fällt dazu gerade kein Befehl ein.) Da die Datei aber für Schreibzugriffe geöffnet werden muss, vermute ich, dass mir dazu (schreiben) in der Hilfe gleich noch Informationen zum Anlegen einer Datei mit gegeben werden. 'Schreiben' heißt im englischen 'write' und Dateioperationen haben etwas mit dem FileSystemObject zu tun. Für mich ist es da am einfachsten, wenn ich meine fso-beispielcode.vbs öffne und nach 'write' suche. Diesmal finde ich nicht gerade das, was ich (schnell) verstehe und bemühe nun doch die 'Windows Script'-Hilfe. Diesmal suche ich (erfolgreich) nach 'writeline'. Folgende Zeilen scheinen sich gut zu eignen:
      Set f = fso.OpenTextFile("c:\test.txt", ForWriting, True)
      f.WriteLine "Hallo Welt!"
Weitere Informationen über 'OpenTextFile' und seine Parameter sind in der Hilfe zu erfahren. Die von mir geänderten Zeilen sehen dann so aus:
      Set FileOut = fso.OpenTextFile( Liste, 2, True)
        FileOut.WriteLine "Hallo Welt!"
        FileOut.Close
      Set FileOut = nothing
Geöffnete Dateien sollten geschlossen werden (close) und nicht mehr benötigte Objektverweise sind zu nichts (nothing) mehr zu gebrauchen. Das Löschen der Objektverweise wurde bisher in diesem Skript unterlassen, da noch nicht abzusehen war, wann sie das letzte mal benutzt werden.

Bei der Suche nach 'Datum' in der Hilfe wird man nach einiger Zeit mehrere Möglichkeiten finden: Now (aktuelles Datum und Zeit) und Time (aktuelle Systemzeit). Ein kurzer Test
      MsgBox now & vbCRLF & time
zeigt, dass Now vorzuziehen ist:
      FileOut.WriteLine Liste & " - Verzeichnis vom " & Now


CDdurchsuchen8 - Inhaltsliste füllen

In Eingabeaufforderung und WSHShell.Run wird erklärt, warum folgende Zeile sinnvoll ist:
      WSHShell.Run "%comspec% /c dir " & CDlw & "\ /s /b >> " & Liste, , True

Da wir schon mal bei WSHShell.Run sind, fügen wir eine weitere Zeile ein:
      WSHShell.Run Liste

Probiert man das Skript jetzt aus, wird zum Abschluß der Skriptabarbeitung die Inhaltsliste (durch WSHShell.Run ) ausgeführt bzw. das Betriebssystem startet entsprechend der Dateiendung eine passende Anwendung (meistens Notepad.exe).

Der Anwender braucht jetzt nur noch [Strg-F] drücken, um die Suchmaske zu öffnen . . . oder das Skript macht dies auch noch:
      WScript.Sleep 1000
      WshShell.SendKeys ( "^F" )


CDdurchsuchen9 - Inhaltsliste als Argument

Wie in den Vorüberlegungen angedeutet, soll es möglich sein, eine Datei mit der Maus auf das Skript zu ziehen und fallen zu lassen. Dadurch erhält das Skript einen Dateinamen als Parameter. Die Suchen in der Hilfe nach 'Argum*' (für 'Argument', 'Argumente', 'Argumenten') bringt bessere Ergebnisse als die nach 'Parameter'. Wenn man die Fundstellen so nach und nach durch gelesen hat, wird man das 'WshArguments-Objekt' als das Mittel zur Lösung finden:
      Set objArgs = WScript.Arguments
      For I = 0 to objArgs.Count - 1
          WScript.Echo objArgs(I)
      Next

Da nur ein Argument gebraucht wird, erfolgt das Verlassen der For .. Next-Schleife mit Exit For bereits im ersten Durchgang vor dem Next:
      Set objArgs = WScript.Arguments
      For i = 0 to objArgs.Count - 1
          Liste = objArgs(i)
          Exit For
      Next

Um sicher zu gehen, dass das übergebene (erste) Argument auch wirklich auf eine Datei verweist, erfolgt kurz eine Überprüfung:
      if fso.FileExists( Liste ) then
Bei 'richtig' müsste die Skriptabarbeitung jetzt am Skriptende weiter machen. Da es kein 'Goto' gibt, werden die erforderlichen Zeilen nach oben kopiert (gibt es dann zweimal im Skript):
      if fso.FileExists( Liste ) then
          WSHShell.Run Liste
          WScript.Sleep 1000
          WshShell.SendKeys ( "^F" )
          WScript.Quit
      End If

Und nicht vergessen: 'objArgs' ist eine neue Variable und muss in einer Dim-Zeile erwähnt werden.


CDdurchsuchenA - Prozedur

Richtig clevere Leute (wie wir) lagern sich wiederholende Zeilen in Prozeduren aus - vergl. Sub-Prozeduren und Function-Prozeduren - eine Einführung. Dazu werden die Zeilen
          WSHShell.Run Liste
          WScript.Sleep 1000
          WshShell.SendKeys ( "^F" )
an das Ende des Skripts verschoben und eingefasst von:
      Sub ListeAnz ( Datei )
          WSHShell.Run Datei
          WScript.Sleep 1000
          WshShell.SendKeys ( "^F" )
      End Sub

Dort, wo die Zeilen vorher standen, steht dann nur noch:
      ListeAnz ( Liste )


CDdurchsuchenB - Inhaltslistenname wählen

Nach einigen Tests fallen die ungünstigen Namen der Inhaltslisten auf. Aus diesem Grund sollte noch eine Abfrage nach dem zu verwendenden Namen eingefügt werden. Ein kurzer Blick in die 'Windows Script'-Hilfen zeigt, dass man das Eingabefeld mit dem dritten Parameter vor belegen kann:
      Liste = InputBox( Text , WScript.ScriptName, Liste )


CDdurchsuchenC  - keine CD-Laufwerksauswahl

Arbeitet man einige Zeit mit dem Skript auf einem Computer mit nur einem CD-Laufwerk, nervt irgendwann die Abfrage nach dem Laufwerk.

Da es keinen Befehl (der mir bekannt ist) gibt, der die Anzahl der CD-Laufwerke zurück gibt, muss man sich etwas 'basteln'. Mir fallen dazu auf die schnelle drei Varianten ein:

1. CDdurchsuchenC1 - keine CD-Laufwerksauswahl 1
In der For .. Next-Schleife zählt eine weitere Variable die Anzahl der CD-Laufwerke. Nach einem anschließenden Vergleich wird bei 1 nocheinmal eine For .. Next-Schleife durchlaufen, die nur ein Laufwerk finden dürfte.

2. CDdurchsuchenC2 - keine CD-Laufwerksauswahl 2
In der For .. Next-Schleife zählt eine Variable CDnr die Anzahl der CD-Laufwerke und eine Variable Text speichert (den zuletzt gefundenen) Laufwerksbuchstaben. Damit enthält Text nach der Schleife den CD-Laufwerksbuchstaben, wenn es nur ein CD-Laufwerk gibt.

3. CDdurchsuchenC3 - keine CD-Laufwerksauswahl 3
Diese Variante würde ich verwenden. In der For .. Next-Schleife wird eine Variable (z.B. Text) eingefügt, die ein Teil der Informationen enthält, die CDlw übergeben werden. Wenn man auf vbTab (in CDlw = CDlw & vbTab & i.DriveLetter...) verzichtet, stimmen nach der For .. Next-Schleife das erste Zeichen in CDlw mit dem Zeichen in der Variable (Text) überein. Für diese Variante würde ich mich entscheiden, weil dazu keine zusätzliche Variable im Skript eingefügt werden muss. Ich finde, mit der Zunahme der Variablen verringert sich die Übersichtlichkeit.


CDdurchsuchenD - Kommentare

Was man eigentlich laufend und bereits während der Skriptprogrammierung machen sollte, haben wir (oder nur ich?) bisher vernachlässigt: Zu den einzelnen Schritten im Skript fehlen Kommentare, die die Funktionsweise des Skripts (CDdurchsuchenC2 - keine CD-Laufwerksauswahl 2) erklären.

Bei der Verwendung von Prozeduren (Sub oder Function) sollte man diese speziell kennzeichnen. Beim Blättern und Suchen nach einer Prozedur im Skript ist eine Linie (mit z.B. Sternchen; *) hilfreich, um den Prozeduranfang zu finden; auch am Prozedurende schreibe ich (fast immer) den Prozedurnamen noch einmal.


CDdurchsuchen - Nachtrag

Das Skript dürfte einigen Ansprüchen gerecht werden. Wer allerdings mehrere Inhaltslisten auf das Skript ziehen will, kann nur die erste durchsuchen.

Um alle übergebenen Listen auswerten zu können, muss man das Skript erheblich umbauen. Im Moment wird ja eine Datei mit Wordpad geöffnet, die dann mit WordPad-eigenen Mitteln durchsucht werden kann. Eine Skriptlösung müsste alle Dateien der Reihe nach öffnen, zeilenweise lesen (und prüfen) und die Fundstellen mit Dateiname (und Zeilennummer?) in eine Ergebniszeile speichern. Dazu könnte man sicher einige Zeilen aus dateizeilenweiselesenbearbeitenschreiben.vbs verwenden. Da die Umbauarbeiten recht umfangreich wären . . . möchte ich hier davon Abstand nehmen.

Zum Schluss noch ein Danke an Peter Zienert für seine Anregungen, Korrekturen und Hilfen.


http://dieseyer.de • all rights reserved • © 2003 v3.7