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