okinami
Goto Top

Batch - Textzeile vor bestimmten Zeichen einfügen

Hallo Zusammen,

Ich würde gerne mit einem Bacth in einer *.ini Datei eine zusätzliche Textzeile einfügen.
Die Datei sieht etwa wie folgt aus:

[text]
text
text
text
text
[text]
text

Wie ist es möglich vor der zweiten [ eine neue Zeile mit einem Text einzufügen?

Würde mich freuen wenn mir jemand weiterhelfen kann.

Content-Key: 97966

Url: https://administrator.de/contentid/97966

Printed on: April 19, 2024 at 04:04 o'clock

Member: bastla
bastla Sep 27, 2008 at 08:26:25 (UTC)
Goto Top
Hallo Okinami und willkommen im Forum!

Soferne sich "[text]" und "[text]" unterscheiden, sollte es etwa so gehen:
@echo off & setlocal
set "iniDatei=D:\Eine.ini"  
set "Markierung=[text]"  
set "Neu=neue Zeile mit einem Text"  

set "Bak=%temp%\Ini.bak"  
move "%iniDatei%" %Bak%  
for /f "tokens=1* delims=:" %%i in ('findstr /n "^" %Bak%') do (set "Zeile=%%j" & call :ProcessLine)  
goto :eof

:ProcessLine
if not defined Zeile (>>"%iniDatei%" echo\ & goto :eof)  
if "%Zeile%" neq "%Markierung%" (>>"%iniDatei%" echo %Zeile% & goto :eof)  
>>"%iniDatei%" echo %Neu%  
>>"%iniDatei%" echo %Zeile%  
goto :eof
Es wird eine Sicherungskopie der ini-Datei als "%temp%\Ini.bak" erstellt.

Grüße
bastla
Member: Okinami
Okinami Sep 27, 2008 at 08:36:34 (UTC)
Goto Top
Hallo bastla,

besten Dank für Deine Hilfe, denn das funktioniert schon fast perfekt. Aber was mache ich, wenn der Inhalt der zweiten [ ] klammer variert?
Was auf jedenfall bleibt ist die Tatsache, das der eingefügte Text vor der zweiten Klammer stehen soll.

[text]
text 1
text 2
text 3
text XYZ
[text]

text XYZ sollte der eingefügte Text sein. Jedoch kann man es auch nicht Zeilen abhänig machen, weil auch diese varieren.

Was wäre wenn ich das ganze mit findstr auswerte und nach der [ suche und dann die Zeile -1 einfüge?

Gruss Okinami
Member: bastla
bastla Sep 27, 2008 at 08:56:20 (UTC)
Goto Top
Hallo Okinami!

Was wäre wenn ich das ganze mit findstr auswerte und nach der [ suche und dann die Zeile -1 einfüge?
Auch eine Möglichkeit - wenn allerdings das gesuchte Zeichen "[" ohnehin als erstes Zeichen in der Zeile steht, sollte es auch so gehen:
@echo off & setlocal enabledelayedexpansion
set "iniDatei=D:\Eine.ini"  
set "Markierung=["  
set "Neu=Das ist die neue Zeile"  
set Pos=2

set /a V=0
set "Bak=%temp%\Ini.bak"  
move "%iniDatei%" %Bak%  
for /f "tokens=1* delims=:" %%i in ('findstr /n "^" %Bak%') do (set "Zeile=%%j" & call :ProcessLine)  
goto :eof

:ProcessLine
if not defined Zeile (>>"%iniDatei%" echo\ & goto :eof)  
if "%Zeile:~,1%"=="%Markierung%" (  
    set /a V+=1
    if !V! equ %Pos% >>"%iniDatei%" echo %Neu%  
)
>>"%iniDatei%" echo %Zeile%  
goto :eof
Noch als kurze Erklärung zum Ablauf:

Wenn innerhalb einer Schleife oder, wie hier, einer If-Anweisung Variablenwerte (konkret: der Zähler %V% - siehe dazu unten) verändert werden, können diese geänderten Werte in dieser If-Anweisung nur dann sofort wieder ausgelesen werden, wenn "delayedExpansion" aktiviert ist - daher der Zusatz in der ersten Zeile.

Die Variable %Pos% gibt an, vor dem wievielten Vorkommen der Markierung die neue Zeile eingefügt werden soll.

Die "for"-Zeile dient dazu, alle Zeilen aus der ini-Datei auszulesen, wobei Leerzeilen verloren gehen würden, wenn nicht mithilfe der Nummerierungsfunktion von "findstr" jede Zeile im Format
Zeilennummer:Zeileninhalt
gelesen würde.

Damit wirklich alle Zeilen erfasst werden, dient mit dem Zeichen "^" der Zeilenanfang als Suchkriterium - einen Anfang hat schließelich jede Zeile, ein Ende, im Sinne einer Zeilenschaltung, muss es aber bei der letzten Zeile einer Datei nicht unbedingt geben.
Da ja nur der Zeileninhalt interessiert, wird durch Angabe des Trennzeichens ("delimiters") ":" diese Zeile zerlegt, wobei "1*" dafür sorgt, dass nur die Teile "vor" sowie der "Rest ("*") nach" dem ersten Trennzeichen gebildet werden - diese stehen dann in den Schleifenvariablen %%i und %%j zur Verfügung.

Der Inhalt von %%j entspricht also der eigentlichen Zeile und wird daher einer entsprechenden Variablen zugewiesen, welche dann im Unterprogramm ":ProcessLine" weiter behandelt wird.

Hier wird zunächst überprüft, ob es sich um eine Leerzeile handelt. Ist dies der Fall, kann mit "echo\" eine Leerzeile in die Datei geschrieben und das Unterprogramm (mit "goto :eof") verlassen werden. Ein "echo %Zeile%" hätte den unerwünschten Effekt, dass, das ja dann der Befehl eigentlich nur "echo" lauten würde, die Statusmeldung "ECHO ist eingeschaltet (ON)." ausgegeben und damit in der Datei landen würde.
Die Sprungmarke ":eof" steht für "Ende" (des Batches - bei ihrer ersten Verwendung in Zeile 11 - oder eines Unterprogrammes, wie eben hier).

War die Zeile nicht leer, wird das erste Zeichen (zum Thema "Teilstrings" siehe "set /?") auf Übereinstimmung mit dem Suchbegriff (%Markierung%) geprüft.

Wird eine Übereinstimmung festgestellt, muss der Zähler %V% (für "Vorkommen") erhöht und danach mit der gewünschten Position verglichen werden. Bei diesem Vergleich wird mit der Schreibweise !V! die bereits angesprochene "delayedExpansion" erzwungen, wodurch der "hochgezählte" Wert richtig erkannt wird. Handelt es sich um die richtige Position, wird die neue Zeile in die Datei geschrieben.

Auf jeden Fall ist aber danach die soeben gelesene Zeile auch noch zu schreiben.

Grüße
bastla
Member: Okinami
Okinami Sep 28, 2008 at 09:09:01 (UTC)
Goto Top
Guten Morgen,

zuerst einmal herzlichen Dank für die ausführliche Erklärung. Nun glaube ich das ganze zu verstehen. ;O)
Jedoch habe ich trotzdem noch eine kleine Frage dazu:

Mit dem Batch mache ich ja zuerst eine Kopie der bestehenden Datei und füge dann Zeile für Zeile von der Kopie in die original Datei mit der zusätzlichen Zeile zurück.

Mit dem Befehl findstr /B /L /I C:[ /N C:\EINE.INI gelingt es, die Zeilennummer der Zeilen die ein "[" enthalten zu nummerieren und auszulesen.
Ist es nun nicht möglich zum Beispiel die Zahl in der zweiten Zeile weiterzuverwenden und dann eine zusätzliche Zeile bei der 2ten Zahl - 1 einzufügen?

Würde mich über eine Antwort freuen...

Gruss Okinami
Member: bastla
bastla Sep 28, 2008 at 09:21:12 (UTC)
Goto Top
Hallo Okinami!

Die Zeilennummer zu kennen hilft nicht wirklich, da Du trotzdem die Zeilen per Schleife auslesen musst - eine Blockverarbeitung wäre nur in der Hinsicht möglich, dass Du eine bestimmte Anzahl von Zeilen am Anfang überspringen kannst (entweder mit "for" und "skip" oder mit "more +"), um so den Teil nach der einzufügeenden Zeile zu erhalten.

Für die Zeilen vor der Einfügeposition musst Du aber dennoch eine Schleife verwenden, welche zeilenweise einliest und die Zeilennummer vergleicht. Da außerdem ein Sprung aus der Schleife in Batch eigentlich nicht vorgesehen (und auch nicht empfohlen) ist, müssten auch die restlichen Zeilen noch durchlaufen werden - insoferne sehe ich keinen wirklichen Sinn in dieser Variante ...

... abgesehen davon: Hat die Datei genug Zeilen, so dass ein etwaiger Performancevorteil einer anderen Lösung überhaupt erkennbar wäre?

Grüße
bastla
Member: bastla
bastla Sep 28, 2008 at 10:20:40 (UTC)
Goto Top
... aber da es Dir irgendwie ein Anliegen zu sein scheint:
@echo off & setlocal
set "iniDatei=C:\Eine.ini"  
set "Markierung=["  
set "Neu=Das ist die neue Zeile"  
set Pos=2
set "Bak=%temp%\Ini.bak"  

set Z=
set /a Pos-=1
for /f "skip=%Pos% delims=:" %%i in ('findstr /B /C:"%Markierung%" /N "%iniDatei%"') do if not defined Z set Z=%%i  
if not defined Z goto :eof

set /a Z-=1
move "%iniDatei%" %Bak%  
for /f "tokens=1* delims=:" %%i in ('findstr /n "^" %Bak%') do  if %%i leq %Z% >>"%iniDatei%" echo\%%j  
>>"%iniDatei%" echo %Neu%  
>>"%iniDatei%" more +%Z% %Bak%  
In Zeile 9 wird (durch Überspringen der ersten [= %Pos%-1] Ergebniszeile) die Zeile mit dem zweiten Vorkommen des Suchbegriffes ermittelt.

Soferne sie gefunden wurde (falls nicht, wird der Batch einfach beeendet), können alle Zeilen, die davor liegen (daher bis inklusive %Z%-1), in die neue Datei geschrieben werden.

Danach wird die neue Zeile angefügt und schließlich der Rest (durch Überspringen der Zeilen bis Zeilennummer %Z%-1 [da diese Berechnung schon vorher erfolgt ist, kann unmittelbar die Variable %Z% verwendet werden]) in die neue Datei geschrieben.

Grüße
bastla

[Edit] Berücksichtigung der Variablen %Pos% ergänzt. [/Edit]
Member: Okinami
Okinami Sep 28, 2008 at 11:00:20 (UTC)
Goto Top
Hallo nochmals,

mittlerweilen existieren bereits zwei Lösung die einwandfrei funktionieren.
Jedoch habe ich noch eine letzte Frage:

Ich möchte verhindern das der Batch zweilmal ausgeführt werden kann. Das mache ich mit dem Befehl

type %iniDatei% | find /I %Neu%
if errorlevel 1 goto start
if errorlevel 0 goto end

:start
Echo Eintag wird erstellt
ping -n 8 localhost>NUL

Diese Zeilen füge ich am Anfang des Batchs ein, also vor der Zeile 01.
Danach folgt einer der beiden "bastla"-Batch ;O)

Am Ende füge ich noch folgendes an:

:end
Echo Eintrag existiert bereits
ping -n 8 localhost>NUL

Man glaubt es kaum, es funktioniert sogar!!

Nun möchte ich am Ende der Schleife weiter Befehle ausführen, zum Beispiel noch eine weiter Zeile am Ende der ini Datei einfügen.
Aber wo setze ich den Befehl, so das er nicht bei jedem Schlaufendurchgang wiederholt wird?

Gruss Okinami
Member: bastla
bastla Sep 28, 2008 at 11:52:47 (UTC)
Goto Top
Hallo Okinami!

Ich möchte verhindern das der Batch zweilmal ausgeführt werden kann.
Die Überprüfung ließe sich etwas knapper so formulieren:
findstr /c:"%Neu%" "%IniDatei%">nul && goto :end  
Nach dieser Zeile kann dann bereits der ":start"-Teil folgen, was einen Sprung dorthin überflüssig macht.

Nun möchte ich am Ende der Schleife weiter Befehle ausführen, zum Beispiel noch eine weiter Zeile am Ende der ini Datei einfügen.
Aber wo setze ich den Befehl, so das er nicht bei jedem Schlaufendurchgang wiederholt wird?
Das Ende des Hauptprogrammes ist im ersten Fall die Zeile 11 ("goto :eof") - daher können alle unmittelbar vor dem Ende (aber nach der Schleife) durchzuführenden Befehle dort eingefügt werden.

Bei der zweiten Version ist es noch einfacher - hier muss ohnehin nach der Schleife noch in die Datei geschrieben werden (und es wird auch, anders als oben, kein Unterprogramm verwendet), sodass weitere Zeilen am Ende des Batches angefügt werden können.

Grüße
bastla