Vinzenz Mai: Wie automatisieren? Mit Windows Scripting Host!

Beitrag lesen

Hallo Ole,

ich suche nach einer Möglichkeit folgendes zu automatisieren:

  1. Finde in Ordner X die Dateien (HTML) in denen der String A (eine Zeile) 2x vorkommt
  2. Gehe zu Fundstelle 2 in dieser Datei und kopiere diese und die Folgenden 19 Zeilen
  3. Ersetze String B (Teil von A) durch String C
  4. Füge den kopierten String an eine bestehende Datei an.

Optimal wäre, wenn ich das ganze quasi in einem einzigen Programmaufruf (sowas wie ne EXE...ja, ihr ahnt es schon...unter Windows) lösen könnte.

Hat jemand einen einfachen Lösungsansatz dafür?

Nein, keinen schnellen Lösungsansatz, aber einen Lösungsansatz.

Btw. Ich möchte davon absehen jetzt auf die Schnelle noch C o.ä. lernen zu müssen ;).

Nun ja, C wäre so ziemlich die letzte Hochsprache, die ich Dir für Zeichenkettenverarbeitung ans Herz legen würde ...

Nun zu meinem Vorschlag: Ich gehe davon aus, dass Du nach Möglichkeit mit Windows-Bordmitteln auskommen willst - und dass es sich um ein neueres Windows aus der NT-Familie handelt. Mein Vorschlag nutzt den Windows Scripting Host, wobei dieser wiederum die Kommandozeile, zum Beispiel CMD.EXE, benutzt.

  1. Schritt:
  1. Finde in Ordner X die Dateien (HTML) in denen der String A (eine Zeile) 2x vorkommt

Diesen Schritt können wir leider nur beinahe von der Kommandozeile erledigen lassen:

FINDSTR /X /N /C:"A" X\*.html

liefert wegen     /X - Suchstring entspricht einer ganzen Zeile     /N - Gib die Zeilennummer vor jeder Trefferzeile an

folgende Ausgabe

<Pfad\zu\Datei>:<zeilennummer>:<suchtext>

  1. Schritt     Schreibe die Ausgabe obigen Befehls in ein Array und verarbeite dieses,     wie erforderlich:

a) Ermittle die Dateien, in denen die Zeichenkette zweimal vorkommt:       <Pfad\zu\Datei> muss mindestens 2x untereinander stehen, d.h.       betrachte jeweils nur die zweite Zeile zu jedem <Pfad\zu\Datei>

b) Verarbeite die so erhaltene Liste zeilenweise:

- Öffne die in der Zeile angegebene Datei und lese diese in ein         Array ein.

  1. Gehe zu Fundstelle 2 in dieser Datei und kopiere diese und die    Folgenden 19 Zeilen

- Kopiere aus dem Inhaltsarray den entsprechenden Ausschnitt (Slice)          in eine Variable

  1. Ersetze String B (Teil von A) durch String C

- Ersetze im Inhaltsarray in der angegebenen Zeile String B durch          String C        - Speichere den geänderten Inhalt ab

  1. Füge den kopierten String an eine bestehende Datei an.

- Öffne die bestehende Datei und hänge den kopierten String an diese          an.

  1. Schritt: Auswahl der Programmiersprache    Der Windows Scripting Host unterstützt eine Reihe von Programmiersprachen,    zum Beispiel:       - VB-Script       - JScript       - Perl       - Python    Die beiden erst genannten sollten Dir auf Deinem Windows-System auf jeden    Fall zur Verfügung stehen. Da Du aus dem Webumfeld kommst, wählen wir    JScript (die Microsoft-Variante von Javascript), da Dir dessen Syntax,    Objekte ... bereits bekannt sind - auch wenn wir uns damit an anderer    Stelle Probleme einhandeln.

  2. Schritt: Umsetzung (ohne Fehlerbehandlung, die ist Dir überlassen)

Der Aufruf soll so erfolgen:

Skriptname Suchzeile Suchstring Ersatzstring Verzeichnis Logdatei

Netter wäre es natürlich, eine kleine HTA mit Eingabefeldern und Fileselectboxen    zu erstellen.


/* ------------------------------------------------------------------------ */

// Wir brauchen ein Shell-Objekt, um das FINDSTR-Kommando abzusetzen
var WshShell = new ActiveXObject("WScript.Shell");

// übergebe die Kommandozeilenparameter an Variablen
// Beachte: Hier gehört grundsätzliche eine Fehlerbehandlung hin!
var suchzeile   = WScript.Arguments(0);
var nadel       = WScript.Arguments(1);
var ersatz      = WScript.Arguments(2);
var verzeichnis = WScript.Arguments(3);
var logfile     = WScript.Arguments(4);

// Beachte: Windows-Verzeichnispfade nutzen den Backslash, der ist in JScript
// Escapezeichen, wir müssen daher in den Pfadangaben die Backslashes verdoppeln
// Dazu nutzen wir eine selbstgeschriebene Funktion os_escape()
verzeichnis     = os_escape(verzeichnis);
logfile         = os_escape(logfile);

// Wir suchen nur HTML-Dateien (hier ggf. korrigieren)
var dateimaske  = verzeichnis + "\*.html";

// Baue das Kommando zusammen, wir lassen es den Kommandozeileninterpreter ausführen
// In der Umgebungsvariablen %COMSPEC% ist der Interpreter zu finden
// /C sorgt dafür, dass er gleich wieder geschlossen wird
// Für die Anführungszeichen ist etwas Jonglieren notwendig
var command = "%COMSPEC% /C \"findstr /X /N /C:\"" + suchzeile + "\" " + dateimaske + "\""

// Führe das Kommando aus ...
var oExec = WshShell.Exec(command);
// ... und lese die Ausgabe in eine Variable ein
var zeilenliste = oExec.StdOut.ReadAll();

// Mögliche Debug-Ausgabe
// WScript.Echo(zeilenliste);

// Wir benötigen von jeder in der Zeilenliste aufgeführten Datei nur diejenigen,
// die mindestens zweimal auftreten - und zwar nur die zweite Fundstelle.
// Dafür ist die Funktion get_Files() zuständig
zeilenliste = get_Files(zeilenliste);
// Debugausgabe (hat's geklappt?)
// WScript.Echo(zeilenliste.join("\n"));

// Arbeite der Reihe nach die Dateien ab:
var anzahl = zeilenliste.length;
for (var i = 0; i < anzahl; i++) {
    // Suchzeile verändern - und Originaltext zurückgeben
 var text = bearbeite_datei(zeilenliste[i], nadel, ersatz);
 // Originaltext loggen
 haenge_text_an_log(logfile, text);
}

/* ------------------------------------------------------------------------
** Helferfunktionen
**
**     string os_escape( string filename )
**     Array  get_Files( string zeilenliste )
**     string bearbeite_datei( string eintrag, string nadel, string ersatz )
**     string get_filename( string eintrag )
**     int get_zeile( string eintrag )
**     Array lese_datei_in_array( string filename )
**     void schreibe_datei( string filename, string inhalt )
**     void haenge_text_an_log(string filename, string inhalt )
** ----------------------------------------------------------------------*/

function os_escape(filename) {
    // Backslashes in Dateinamen müssen verdoppelt werden.
 // quick and dirty
 return filename.replace("\\", "\\\\");
}

function get_Files(zeilenliste) {
    // filtert aus der zeilenliste (eine Zeichenkette) die gewünschten Zeilen
 // aus, d.h. jeweils die Zeile, in der das zweite Auftreten des Suchstrings
 // innerhalb einer Datei vermerkt ist.
 var ausgabe = new Array();
 var zeilen = zeilenliste.split("\n");
 var anzahl = zeilen.length;
 var merker = "";
 var auftreten = 0;
 for ( var i = 0; i < anzahl; i++) {
     // Abschnitte sind durch Doppelpunkt (:) getrennt
  // Vorsicht, Laufwerksbuchstabe kann ein Problem sein
        var inhalt = zeilen[i].split(":");
  var filename = "";
  if (inhalt.length == 3) {
      // relative Pfadangabe
   filename = inhalt[0];
  }
  else {
   // Angabe mit Laufwerksbuchstabe
   filename = inhalt[0] + ":" + inhalt[1];
  }
  if (filename != merker) {
   // Erstes Auftreten,
   // Setze merker auf neuen Wert
   merker = filename;
   // Setze auftreten auf 1
   auftreten = 1;
  }
  else {
      auftreten++;
   if (auftreten == 2) {
    // Diese Zeile benötigen wir
    ausgabe.push(zeilen[i]);
   }
   // alle anderen interessieren auch nicht mehr :-)
  }
    }
 return ausgabe;
}

function bearbeite_datei(eintrag, nadel, ersatz) {
   // Kopiert aus einer Datei die Zeile mit dem Suchstring und
   // die folgenden 19 Zeilen in eine Variable
   // Ersetzt in der Suchzeile die Zeichenkette nadel durch die
   // Zeichenkette ersatz
   // Schreibt die geänderte Datei weg
   // Gibt den gewünschten Text zurück

   // Benutzt folgende Helferfunktionen
   // - get_filename(eintrag)
   //   ermittelt aus einer der Ausgabe von FINDSTR /X /N entsprechenden
   //   Zeile den Dateinamen
   // - get_zeile(eintrag)
   //   ermittelt aus ebensolchem Eintrag die Zeilennummer der Suchzeile
   // - lese_datei_in_array(filename)
   //   das Äquivalent zu file() in PHP
   //   ausser dass die Zeilenenden bereits entfernt sind :-)
   // - schreibe_datei(filename, inhalt)
   //   das Äquivalent zu file_put_contents() in PHP

   var filename;
   var zeile;
   var inhalt;
   var kopierbereich;
   var offset = 19;

   // Ermittele die zu öffnende Datei
   filename = get_filename(eintrag);
   // Ermittle die Zeile, in der der Suchstring zum zweiten Mal in der Datei vorkommt
   zeile    = get_zeile(eintrag);

   // Debug-Ausgabe
   // WScript.Echo(zeile);

   // Lese die zu bearbeitende Datei in ein Array ein
   inhalt   = lese_datei_in_array(filename);

   // Ermittle zu loggenden Text
   kopierbereich = inhalt.slice(zeile - 1, zeile + offset);
   kopierbereich = kopierbereich.join("\n");
   WScript.Echo(kopierbereich);

   // Verändere die Suchzeile
   inhalt[zeile-1] = inhalt[zeile-1].replace(nadel, ersatz);
   // WScript.Echo(inhalt[zeile-1]);
   // Schreibe geänderte Datei weg
   schreibe_datei(filename, inhalt.join("\n"));

   // Gebe gesuchte Zeichenkette zurück
   return kopierbereich;
}


function get_filename(eintrag) {
 var inhalt = eintrag.split(":");
 var filename = inhalt[0];
 if (inhalt.length == 4) {
  // Dateiname mit Laufwerksbezeichnung)
  filename = inhalt[0] + ":\\" + inhalt[1];
 }
 return os_escape(filename);
}

function get_zeile(eintrag) {
 var inhalt = eintrag.split(":");
 if (inhalt.length == 4) {
  return parseInt(inhalt[2]);
 }
 else {
  return parseInt(inhalt[1]);
 }
}

function lese_datei_in_array(filename) {
    var fso, f, inhalt;
    var ForReading = 1, ForWriting = 2;

    // Wir brauchen ein FileSystemObject
    fso = new ActiveXObject("Scripting.FileSystemObject");
    // Oeffnen die Datei zum Lesen
    f = fso.OpenTextFile(filename, ForReading);
    // Lese Dateiinhalt in eine Zeichenkette
    inhalt = f.ReadAll();
    // Wandle diese in ein Array aller Zeilen um
    inhalt = inhalt.split("\n");
    // Schliessen die Datei
    f.Close();

    // Geben den Inhalt in Form des Arrays aller Zeilen zurück
    return inhalt;
}

function schreibe_datei(filename, inhalt) {
    var fso, f, inhalt;
    var ForReading = 1, ForWriting = 2;

    // Wir brauchen ein FileSystemObject
    fso = new ActiveXObject("Scripting.FileSystemObject");
    // Oeffnen die Datei zum Schreiben
    f = fso.OpenTextFile(filename, ForWriting);
    // Schreiben den Inhalt in die Datei
    f.Write(inhalt);
    // Schliessen die Datei
    f.Close();
}

function haenge_text_an_log(filename, text) {
   var fso, f;
   var ForAppending = 8;

   // Wir brauchen ein FileSystemObject
   fso = new ActiveXObject("Scripting.FileSystemObject");
   // Oeffnen eine Datei zum Anhaengen
   f = fso.OpenTextFile(filename, ForAppending);
   // Fuegen den Text am Ende an
   f.Write(text);
   // Schliessen die Datei
   f.Close();
}

Wie bereits gesagt, die Fehlerbehandlung habe ich komplett Dir überlassen :-) Ein einfacher Test an der Kommandozeile hat bei mir das gewünschte Ergebnis erzielt. Du solltest Dir aber wirklich eine einfache HTML-Oberfläche dazu basteln und das ganze als HTA abspeichern.

Mit freundlichen Grüßen

Vinzenz