Ole: Wie automatisieren?

Hallo,

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?
Btw. Ich möchte davon absehen jetzt auf die Schnelle noch C o.ä. lernen zu müssen ;).

Grüße
Ole
(8-)>

--
Das Wort Vegetarier kommt aus dem Indianischen und bedeutet: Zu dumm zum Jagen.
  1. Mahlzeit,

    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.

    Unter Windows? Viel Spaß, Sisyphos.

    Normalerweise würde ich für solche Geschichten eine Kombination von find, grep, cut und sed vorschlagen ... oder halt Perl.

    MfG,
    EKKi

    --
    sh:( fo:| ch:? rl:( br:> n4:~ ie:% mo:} va:) de:] zu:) fl:{ ss:) ls:& js:|
    1. Hi

      Normalerweise würde ich für solche Geschichten eine Kombination von find, grep, cut und sed vorschlagen ... oder halt Perl.

      Hmmm...gab es da nicht die Unixtools for Windows ...

      Grüße
      Ole
      (8-)>

      --
      Das Wort Vegetarier kommt aus dem Indianischen und bedeutet: Zu dumm zum Jagen.
      1. Hallo

        Normalerweise würde ich für solche Geschichten eine Kombination von find, grep, cut und sed vorschlagen ... oder halt Perl.

        Hmmm...gab es da nicht die Unixtools for Windows ...

        Cygwin ist erste Sahne.
        Mich hat hier allerdings die ausschließliche Verwendung von Windows-Bordmitteln herausgefordert.

        Freundliche Grüße

        Vinzenz

  2. 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.

    Als Mac-User hättest du nun einen Hinweis auf den »Automator« erhalten, mit dem du sowas in ein paar Klicks (GUI, Baby!) hättest umsetzen können.

    *stichel* ;)

    Viele Grüße!
    _ds

    --
    Soraya, die ihre Tochter seit 25 Jahren schlicht »Saaraah« statt »Szäruah« nennt, vermittelt den Eindruck der Tourmanagerin und kümmert sich gemeinsam mit dem wenig homophoben Wedding-Planner Frank um die Gästeliste der anstehenden Traumhochzeit.
    Medienrauschen, In Love: Henne. Hahn.
    1. Hi

      *stichel* ;)

      *grummel* ;)

      Grüße
      Ole
      (8-)>

      --
      Das Wort Vegetarier kommt aus dem Indianischen und bedeutet: Zu dumm zum Jagen.
  3. 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

    1. Hallo Vizenz,

      meine Güte...da bin ich aber erstmal sprachlos :)

      [...]

      Ich werde mich mal durch die Scripte durchwühlen. Bin schwer beeindruckt.

      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.

      Sobald ich mich tiefer in die Materie eingearbeitet habe, werde ich das in Angriff nehmen.

      Vielen, vielen Dank.

      Grüße
      Ole
      (8-)>

      --
      Das Wort Vegetarier kommt aus dem Indianischen und bedeutet: Zu dumm zum Jagen.
      1. Hallo Ole,

        Ich werde mich mal durch die Scripte durchwühlen.

        falls Du's noch nicht getan hast, wühl Dich durch die neue Version durch.
        Die ist schon mal eine Klasse besser.

        Bin schwer beeindruckt.

        Danke!

        Du solltest Dir aber wirklich eine einfache HTML-Oberfläche dazu basteln
        und das ganze als HTA abspeichern.

        das neue Skript sollte sich viel leichter über eine HTA steuern lassen.
        Noch ein Tipp: Code für eine Verzeichnisauswahlbox findest Du auf meiner Testseite.

        Freundliche Grüße

        Vinzenz

        1. Guten Morgen,

          falls Du's noch nicht getan hast, wühl Dich durch die neue Version durch.
          Die ist schon mal eine Klasse besser.

          Auch wenn ich mich wiederhole:

          Bin schwer beeindruckt.

          das neue Skript sollte sich viel leichter über eine HTA steuern lassen.
          Noch ein Tipp: Code für eine Verzeichnisauswahlbox findest Du auf meiner Testseite.

          Ich arbeite noch daran das Ganze zu verstehen, ist ja weitgehend Neuland für mich...aber ich habe schlaue Bücher und das Internet...die Helfen :)

          Danke :)
          Ole
          (8-)>

          --
          Das Wort Vegetarier kommt aus dem Indianischen und bedeutet: Zu dumm zum Jagen.
          1. Hallo Ole,

            das neue Skript sollte sich viel leichter über eine HTA steuern lassen.
            Noch ein Tipp: Code für eine Verzeichnisauswahlbox findest Du auf meiner Testseite.

            Ich arbeite noch daran das Ganze zu verstehen,

            wenn Du spezielle Fragen hast, stelle sie doch hier.

            ist ja weitgehend Neuland für mich...

            Wieso den das. Das meiste ist einfaches Javascript-kompatibles JScript.
            Ich bin mir sicher, dass Du Dich darin ganz ordentlich auskennst :-)

            aber ich habe schlaue Bücher
            und das Internet...die Helfen :)

            z.B. dieses Forum.

            Ich habe gestern abend noch ein Problem entdeckt, für das ich bisher keine
            befriedigende Lösung gefunden habe:

            Doppelte Anführungszeichen in Suchzeile, Such- und Ersatzzeichenkette.

            Der Windows Scripting Host schluckt diese bei der Übergabe auf der Kommandozeile.
            Meine recht intensive Internet-Recherche zu diesem Thema war bisher erfolglos.

            FINDSTR akzeptiert durchaus doppelt Anführungszeichen, diese müssen mit einem
            Backslash maskiert werden, das ist also nicht das Problem. Man kriegt sie als
            Kommandozeilenparameter nicht in die Anwendung.

            Zwei Lösungen:
            a) Erstelle eine HTA
               Auf die Inhalte von Formularfelder kann problemlos zugegriffen werden.
            b) Statt einer Serie von Parametern erfolgt die Parameterübergabe in Form
               z.B. einer INI-Datei. Aus Dateien können Zeichenketten mit doppelten
               Anführungszeichen problemlos eingelesen werden.

            Todo:
            Die Anführungszeichen müssen beim Aufruf von FINDSTR noch maskiert werden.

            Freundliche Grüße

            Vinzenz

    2. Hallo Vinzenz,

      Bin ebenfalls sehr beeindruckt. Gleich ein fertiges Programm, wow! Das Windows-Scripting kenne ich bis jetzt kaum (nur innerhalb von MS-Word), aber das ist ja jetzt ein super Studienmaterial, incl. Doku, vielen Dank, das kann ich sicher noch brauchen.

      Ich persönlich hätte sowas ja mit Perl gelöst, zumindest teilweise, aber nur mit Bordmitteln ist auch nicht schlecht.

      Gruß, Don P

      1. Hallo Don P,

        Bin ebenfalls sehr beeindruckt. Gleich ein fertiges Programm, wow!

        danke.

        Ich persönlich hätte sowas ja mit Perl gelöst, zumindest teilweise, aber nur mit Bordmitteln ist auch nicht schlecht.

        Das war genau die Herausforderung, die mich motiviert hat.
        Inzwischen hab' ich eine verbesserte Variante erstellt, siehe https://forum.selfhtml.org/?t=160894&m=1047574.
        Eigentlich wollte ich ja den Postingtitel ändern. Da ich das Skript noch etwas zusammenstreichen musste, damit es die Grenzen der Forumssoftware noch einhält, hab' ichs im zweiten Anlauf vergessen.

        Freundliche Grüße

        Vinzenz

    3. Hallo,

      Ich habe meinen Code etwas revidiert.

      Neu:

      • mit Leerzeichen in Pfadangaben umgehen
      • prinzipiell beliebig viele Folgezeilen mitloggen
      • auch Unterverzeichnisse bearbeiten (true statt false im Aufruf)
      • auch Dateien mit anderen Endungen bearbeiten
      /**
       * Schreibt den übergebenen Inhalt in die angegebene Datei. Falls die Datei nicht existiert,
       * wird sie angelegt.
       * <b>write_to_file</b>
       *
       * @param filename {string} Name der Datei, in die geschrieben werden soll
       * @param content  {string} Inhalt, der in die Datei geschrieben werden soll
       * @param mode     {int}    entweder 2 (Inhalt wird überschrieben) oder 8 (Inhalt wird angehängt)
       */
      function write_to_file(filename, content, mode) {
          var fso, f;
          fso = new ActiveXObject("Scripting.FileSystemObject");
          f = fso.OpenTextFile(filename, mode);
          f.Write(content);
          f.Close();
      }
      
      /**
       * Liest den Inhalt der angegebenen Datei in ein Array aller Zeilen.
       * Die Zeilenenden werden dabei entfernt.
       * Für die Fehlerbehandlung gilt das Gleiche wie bei write_to_file :-(
       * <b>file</b>
       *
       * @param {string} filename Name der Datei, die eingelesen werden soll
       * @return         den Inhalt der Datei als Array der Zeilen
       */
      function file(filename) {
          var fso, f, inhalt;
          fso = new ActiveXObject("Scripting.FileSystemObject");
          f = fso.OpenTextFile(filename, 1);  // 1: Nur-Lese-Modus
          inhalt = f.ReadAll();
          f.Close();
          return inhalt.split("\n");
      }
      
      /**
       * Extrahiert aus der übergebenen Zeichenkette die "Spalte" mit der Zeilennummer.
       * Die übergebenen Zeichenketten sind vom Format:
       *     D:\test\t3.html:1:Das ist ein Text
       * <b>extract_line_no</b>
       *
       * @param  output_findstr {string} eine Zeile der Ausgabe von FINDSTR /X /N
       * @return                         die Zeilennummer als Integer
       */
      function extract_line_no(output_findstr) {
          var content = output_findstr.split(":");
          // Die Zeilennummer steht in der vorletzten Spalte
          // Beachte die explizite Umwandlung in eine Integer
          return parseInt(content[content.length - 2]);
      }
      
      /**
       * Extrahiert aus der übergebenen Zeichenkette den Dateinamen.
       * Die übergebenen Zeichenketten sind vom Format:
       *     D:\test\t3.html:1:Das ist ein Text
       *     D:\test\t3.html:3:Das ist ein Text
       * <b>extract_filename</b>
       *
       * @param  output_findstr {string} eine Zeile im Format der Ausgabe von FINDSTR /X /N
       * @return den Dateinamen
       */
      function extract_filename(output_findstr) {
          var parts = output_findstr.split(":");
          var filename = parts[0];
          if (parts.length == 4) {
              // Dateiname mit Laufwerksbezeichnung)
              filename = parts[0] + ":" + parts[1];
          }
          return filename;
      }
      
      /**
       * Windowsdateinamen verwenden den Backslash als Trennzeichen. Der Backslash ist in
       * JScript jedoch Maskierungszeichen.
       * <b>os_escape</b>
       *
       * @param path {string} Dateiname
       * @return              Zeichenkette mit verdoppelten Backslashes
       */
      function os_escape(path) {
       return path.replace("\\", "\\\\");
      }
      
      /**
       * Kopiert aus der durch filename spezifizierten Datei die Zeile mit dem Suchstring
       * und die folgenden Zeilen, deren Anzahl durch den Parameter offset bestimmt wird.
       * In der Suchzeile wird das Vorkommen von search durch den Inhalt von replace ersetzt.
       * Der Inhalt der Originalzeile und die offset folgenden wird zurückgegeben.
       * <b>process_file</b>
       *
       * @param filename {string}  Datei, die zu bearbeiten ist
       * @param line_no  {integer} Zeilennummer der gesuchten Zeile
       * @param search   {string}  Suchstring innerhalb der Zeile
       * @param replace  {string}  Ersatz für den Suchstring
       * @param offset   {integer} Anzahl der folgenden Zeilen, die benötigt werden
       * @return                   String mit der Originalzeile und den gewünschten folgenden Zeilen
       */
      function process_file(filename, line_no, search, replace, offset) {
         // Lese die zu bearbeitende Datei in ein Array ein
         var content = file(os_escape(filename));
      
         // Kopiere zu loggende Zeilen ...
         // Beachte: FINDSTR zählt die Zeilen von 1 im Gegensatz zu JScript
         result = content.slice(line_no - 1, line_no + offset);
         // ... und wandle diese in eine Zeichenkette um
         result = result.join("\n");
      
         // Ersetze in der Suchzeile das Auftreten von search durch den Inhalt von replace
         content[line_no-1] = content[line_no-1].replace(search, replace);
      
         // Schreibe geänderte Datei weg, überschreibe bisherigen Inhalt
         write_to_file(filename, content.join("\n"), 2);
      
         return result;
      }
      
      /**
       * Filtert aus der Ausgabe von FINDSTR /X /N genau die Zeilen heraus, die das zweite
       * Vorkommen der Suchzeile in der gleichen Datei angeben.
       * <b>filter_list</b>
       *
       * @param output_findstr {string} Ausgabe von FINDSTR /X /N
       * @return                        Array der relevanten Zeilen
       */
      function filter_list(output_findstr) {
          // nimmt die Einträge für den Rückgabewert entgegen
       var filtered_list = new Array();
       // für den Dateinamen der zuletzt bearbeiteten Zeile, anfangs leer
       var previous = "";
       // der wievielte Eintrag zur gleichen Datei
       var count = 0;
      
       // Splitte Ausgabe in Array auf
       var rows = output_findstr.split("\n");
       // Wieviele Zeilen hatte die Ausgabe von FINDSTR
       var rowcount = rows.length;
      
       // Durchlaufe die Zeilen der Ausgabe
       for ( var i = 0; i < rowcount; i++) {
           // Abschnitte sind durch Doppelpunkt (:) getrennt
        // Vorsicht, Laufwerksbuchstabe kann ein Problem sein
              var parts = rows[i].split(":");
        var current = parts[0];
        if (parts.length == 4) {
         // Angabe mit Laufwerksbuchstabe
         current = parts[0] + ":" + parts[1];
        }
        if (current != previous) {
         // Erstes Auftreten: setze previous auf neuen Wert, initialisiere Zähler
         previous = current;
         count = 1;
        }
        else {
            // Suchzeile mehrfach in der gleichen Datei vorhanden: zähle mit
            count++;
         if (count == 2) {
          // Wir sind genau am zweiten Auftreten interessiert
          filtered_list.push(rows[i]);
         }
         // alle anderen fallen genauso unter den Tisch wie das erste Auftreten :-)
        }
          }
       return filtered_list;
      }
      
      /**
       * Setzt in einer Kommandozeile FINDSTR /X /N mit den notwendigen Aufrufparametern ab.
       * Gibt die Ausgabe des Kommandos zurück
       * <b>get_output_findstr</b>
       *
       * @param searchrow {string}  Suchzeile, die exakt übereinstimmen muss
       * @param directory {string}  Verzeichnis, in dem gesucht werden soll
       * @param filemask  {string}  Dateiendung der Dateien, die durchsucht werden sollen
       * @param recursive {boolean} Sollen Unterverzeichnisse mit durchsucht werden
       * @return    Zeichenkette mit der Ausgabe des FINDSTR-Kommandos
       */
      function get_output_findstr(searchrow, directory, filemask, recursive) {
          // Wir brauchen ein Shell-Objekt, um das FINDSTR-Kommando abzusetzen
          var WshShell = new ActiveXObject("WScript.Shell");
      
          // Schalter, die für FINDSTR zu setzen sind:
          // /X               Gibt Zeilen aus, die vollkommen übereinstimmen
          // /N               Gibt die Zeilennummer vor jeder Trefferzeile an
          // /C:Zeichenfolge  Sucht die Zeichenfolge buchstabengetreu
          var options  = "/X /N ";
      
          // /S: Durchsuche auch alle Unterverzeichnisse
          if (recursive) {
              options += "/S ";
          }
      
          // Baue das Kommando für den Kommandozeileninterpreter zusammen
          // 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:
          // Das Kommando ist in doppelte Anführungszeichen zu setzen, der gesuchte Text
          // übrigens auch und genauso die Pfadangabe für die Suche
          var command = "%COMSPEC% /C "
              + "\""                            // Anfang Kommando
              + "FINDSTR "
              + options
              + "/C:"
              + "\"" + searchrow + "\""         // Suchtext in doppelten Anführungszeichen
              + " "                             // Trenne Suchtext und zu durchsuchende Dateien
              + "\"" + os_escape(directory + "\\" + filemask) + "\""
              + "\""                            // Ende Kommando
      
          // Führe das Kommando aus ...
          var oExec = WshShell.Exec(command);
          // ... und gebe die Ausgabe (Kanal STDOUT, nicht STDERR) zurück
          return oExec.StdOut.ReadAll();
      }
      
      /**
       * Überprüft die Eingabeparameter.
       * Aufruf ist wie folgt:
       *     Skriptname searchrow search replace directory logfile
       * Folgende Prüfungen werden vorgenommen:
       *     Anzahl der Parameter muss 5 sein                    32768
       *     searchrow - darf keinen Zeilenumbruch enthalten     32769
       *     search    - muss in searchrow enthalten sein        32770
       *     replace   - darf keinen Zeilenumbruch enthalten     32771
       *     directory - muss ein Verzeichnis sein               32772
       *     logfile   - muss ein gültiger Dateipfad sein        32773
       * Kein Fehler                                             0
       * <b>check_input</b>
       *
       * @param {array} Liste der Aufrufparameter
       * @return        Errorcode
       */
      function check_input(args) {
          // Anzahl der Parameter muss 5 sein
          if(args.length != 5) {
              return 32768;
          }
          // im Text der Suchzeile darf kein Zeilenumbruch enthalten sein
          if(args(0).indexOf("\n") != -1){
              return 32769;
          }
          // der zu ersetzende Text muss in der Suchzeile enthalten sein
          if(args(0).indexOf(args(1)) < 0){
              return 32770;
          }
          // der neue Text darf keinen Zeilenumbruch enthalten
          if(args(2).indexOf("\n") != -1){
              return 32771;
          }
          // Prüfungen auf Verzeichnis und Datei benötigen ein FSO
          var fso = new ActiveXObject("Scripting.FileSystemObject");
      
          // Das angegebene Verzeichnis muss existieren
          if (!fso.FolderExists(os_escape(args(3)))){
              return 32772;
          }
      
          // Die angegebene Logdatei auch :-)
          if (!fso.FileExists(os_escape(args(4)))){
              return 32773;
          }
          return 0;
      }
      
      /*
       * <b>main</b>
       */
      function main(args, filemask, offset, recursive) {
          // Überprüfe die Aufrufparameter des Skripts
       var errorcode = check_input(args);
       if (errorcode > 0) {
              // Fehlerhafte Aufrufparameter
           return(errorcode);
       }
       // Suche mit FINDSTR im angegebenen Verzeichnis in allen Dateien mit der
       // angegebenen Dateiendung, ggf. auch in Unterverzeichnissen
       var output    = get_output_findstr(args(0), args(3), filemask, recursive);
       // Filtere die Ausgabe, so dass nur noch die relevanten zweiten Zeilen
       // jeder Datei übrigbleiben
       output        = filter_list(output);
      
       // Durchlaufe die gefilterte Dateiliste:
       var count     = output.length;
       for ( var i=0; i < count; i++){
           var filename = os_escape(extract_filename(output[i]));
           var line_no  = extract_line_no(output[i]);
           // Extrahiere den zu loggenden Text, Suchzeile plus folgende Zeilen (Anzahl = offset),
           // und schreibe die geänderte Datei weg
           var logged_text = process_file(filename, line_no, args(1), args(2), offset);
           // Hänge den gewünschten Text an die Logdatei an
           write_to_file(os_escape(args(4)), logged_text, 8);
       }
       return 0;
      }
      
      /**
       * Nutze das Skript wie vorgesehen:
       * Es werden HTML-Dateien durchsucht,
       * Die Anzahl der ebenfalls zu loggenden Folgezeilen ist 19
       * Unterverzeichnisse werden nicht durchsucht
       */
      var args      = WScript.Arguments;
      var errorcode = main(args, "*.html", 19, false);
      WScript.Echo(errorcode);
      

      Freundliche Grüße Vinzenz