Batch - setlocal EnableDelayedExpansion - ersetzen von Text, der ein Ausrufezeichen enthält
a) Batch-Variable: !-Zeichen durch ! ersetzen (wurde verworfen)
b) zwischen den setlocal-Modi DisableDelayedExpansion und EnableDelayedExpansion wechseln
Der Beitrag, inkl. Quelltext, ist Gemeinfrei (er darf ohne jegliche Einschränkung genutzt werden).
Hallo.
Ich habe ein Problem damit, einen String durch einen anderen zu ersetzen, wenn der Text ein Ausrufezeichen enthält. Hier ein Beispielcode, der in einer Textdatei sämtliches Aufkommen von „foo“ in „bar“ ersetzt:
Die Ersetzung funktioniert, jedoch werden dabei auch sämtliche Ausrufezeichen, die die Datei möglicherweise enthält, eliminiert.
Mir ist klar, dass ein Bug in setlocal EnableDelayedExpansion die Ursache des Problems ist. Ich suche nur eine Lösung dafür, da der Bug schon sein Jahren existiert und kaum noch damit zu rechnen ist, das MS ihn je fixen wird. Eine Möglichkeit, unter diesen Umständen ein Ausrufezeichen zuzuweisen und auszugeben wäre:
Mein erster Ansatz sah deshalb vor, sämtliche Ausrufezeichen in _FncReplaseString_TextLine durch „!“ zu ersetzen, bevor die Zeile ausgegeben wird. Und genau dafür suche ich nach einer Lösung. Mir wollte das bisher nicht gelingen. Kann mir jemand von euch dabei auf die Sprünge helfen? Oder hat hier einer eine vollkommen andere Idee, wie sich das realisieren läßt?
Bye, nz
Nachtrag: Hier der etwas aufbereitete Quellcode, der das Problem behebt. -- NeonZero 20.08.2008 22:58
Hinweis: Einige Textdateien, wie z.B exportierte .reg-Dateien, liegen im UNICODE-Format vor. Da die Konsole nicht mit UNICODE zurechtkommt, konvertiert die Funktion Textdateien mithilfe von type automatisch in das für die Konsole verständliche ANSI-Format. Andernfalls würde der Text verstümmelt werden.
Für den Registryeditor (regedit.exe) stellt die Konvertierung kein Problem dar; er kann auch ANSI-formatierte .reg-Dateien importieren. Zu beachten bleibt aber, dass UNICODE über den Zeichensatz von ANSI hinaus weitere Zeichen darstellen kann. Auch wenn das eher selten vorkommt, könnten bei der Konvertierung von UNICODE zu ANSI Sonderzeichen verloren gehen. Testen lässt sich das, indem man die Quelldatei (hier test.reg ) zuvor von Hand in das ANSI-Format konvertiert und wieder zurückwandelt, um sie dann mit dem Original zu vergleichen:
Version 1.0.5: Es wurde eine Optimierung vorgenommen (
Version 1.0.4: UNICODE-formatierte Dateien werden nun gemäß dem obigen Hinweis zuvor nach ANSI konvertiert. -- NeonZero 05.09.2008
Version 1.0.3: Der erste Teil der Funktion (String im uebergebenen Text austauschen und der Variablen (%1) zuweisen) wurde nun ebenfalls entsprechend der "!"-Problematik angepaßt. Dabei findet auch der unten stehende Hinweis von bastla und Biber betreffs endlocal & set "Var=%Var%" Anwendung. -- NeonZero 26.08.2008
Version 1.0.2: Der Quelltext wurde nach einem Hinweis von bastla nochmals angepaßt (endlocal hinzugefügt), um den unten beschriebenen Fehler zu beseitigen. -- NeonZero 22.08.2008
Version 1.0.1: Ein etwas aufbereiteter Quellcode, der das eingangs beschriebene Problem von Version 1.0.0 behebt. -- NeonZero 20.08.2008
b) zwischen den setlocal-Modi DisableDelayedExpansion und EnableDelayedExpansion wechseln
Tipp: Wird der Inhalt in den Quelltextfenstern chaotisch angezeigt, kann es hilfreich sein dort auf den Link "Quelltext" (oben rechts) zu klicken. Es öffnet sich dann ein separates Fenster, welches in der maximierten Darstellung, je nach Bildschirmauflösung, den unerwünschten Zeilenumbruch unterbindet.
Hallo.
Ich habe ein Problem damit, einen String durch einen anderen zu ersetzen, wenn der Text ein Ausrufezeichen enthält. Hier ein Beispielcode, der in einer Textdatei sämtliches Aufkommen von „foo“ in „bar“ ersetzt:
@echo off & setlocal EnableDelayedExpansion
call :FncReplaseString "foo" "bar" "d:\tmp\test.txt"
goto :EOF
:FncReplaseString {{comment_single_line_remark:0}}
::Parameters: <FromString> <ToString> <FileName>
:: or: <VarNameToReturnReplasedText> <FromString> <ToString> <TextToReplase>
if not "%~4" == "" ( ::String im übergebenen Text austauschen
set _FncReplaseString_Text=%~4
set %~1=!_FncReplaseString_Text:%~2=%~3!
rem Alternativ dazu: call set %~1=%%_FncReplaseString_Text:%~2=%~3%%
) else if exist "%~3" ( ::String in einer Text-Datei austauschen
set _FncReplaseString_TmpFile=%TMP%\%~n0_out%RANDOM%.tmp
for /F "tokens=1* delims=:" %%a in ('findstr /n $ %3') do (
if not "%%b" == "" (
set _FncReplaseString_TextLine=%%b
set _FncReplaseString_TextLine=!_FncReplaseString_TextLine:%~1=%~2!
echo.!_FncReplaseString_TextLine!
) else (echo.)
) >>!_FncReplaseString_TmpFile!
move "!_FncReplaseString_TmpFile!" "%~3"
)
exit /b 0
Die Ersetzung funktioniert, jedoch werden dabei auch sämtliche Ausrufezeichen, die die Datei möglicherweise enthält, eliminiert.
Mir ist klar, dass ein Bug in setlocal EnableDelayedExpansion die Ursache des Problems ist. Ich suche nur eine Lösung dafür, da der Bug schon sein Jahren existiert und kaum noch damit zu rechnen ist, das MS ihn je fixen wird. Eine Möglichkeit, unter diesen Umständen ein Ausrufezeichen zuzuweisen und auszugeben wäre:
@echo off & setlocal EnableDelayedExpansion
set _x=Hallo^^!
echo _x=!_x!
------------------
Ausgabe: _x=Hallo!
Mein erster Ansatz sah deshalb vor, sämtliche Ausrufezeichen in _FncReplaseString_TextLine durch „!“ zu ersetzen, bevor die Zeile ausgegeben wird. Und genau dafür suche ich nach einer Lösung. Mir wollte das bisher nicht gelingen. Kann mir jemand von euch dabei auf die Sprünge helfen? Oder hat hier einer eine vollkommen andere Idee, wie sich das realisieren läßt?
Bye, nz
Nachtrag: Hier der etwas aufbereitete Quellcode, der das Problem behebt. -- NeonZero 20.08.2008 22:58
@echo off
call :FncReplaseString "foo" "bar" "d:\tmp\test.txt"
goto :EOF
:FncReplaseString {{comment_single_line_remark:0}}
::Parameters: <FromString> <ToString> <FileName>
:: or: <VarNameToReturnReplasedText> <FromString> <ToString> <TextToReplase>
::Note: "="-Characters are not allowed in FromString
::Example: call :FncReplaseString _Var "foo" "bar" "the foo" (_Var="the bar")
:: call :FncReplaseString "foo" "bar" "d:\text.txt" (replase string in text-File)
setlocal DisableDelayedExpansion &::damit bei der Wertzuweisung keine Ausrufezeichen verloren gehen
if "%~4" == "" goto :LEB_FncReplaseString_File
::String im uebergebenen Text austauschen und der Variablen (%1) zuweisen
set "_FncReplaseString_Text=%~4"
setlocal EnableDelayedExpansion &::ermoeglicht die folgende Syntax
set _FncReplaseString_Text=!_FncReplaseString_Text:%~2=%~3!
rem Alternativ dazu: call set %~1=%%_FncReplaseString_Text:%~2=%~3%%
::die beiden oben und hier geoeffneten setlocal-Instanzen schließen & Variable uebernehmen:
endlocal & set "_FncReplaseString_Text=%_FncReplaseString_Text%"
endlocal & set "%~1=%_FncReplaseString_Text%"
exit /b 0
:LEB_FncReplaseString_File
set _FncReplaseString_TmpFile=%TMP%\%~n0_out%RANDOM%.tmp
if exist "%~3" ( ::String in einer Text-Datei austauschen
for /F "tokens=1* delims=:" %%a in ('type %3 ^| findstr /n $') do (
::type konvertiert UC nach ANSI; findstr verhindert, dass Leerzeilen verloren gehen
if not "%%b" == "" (
set _FncReplaseString_TextLine=%%b
setlocal EnableDelayedExpansion &::ermoeglicht die folgende Syntax
set _FncReplaseString_TextLine=!_FncReplaseString_TextLine:%~1=%~2!
echo.!_FncReplaseString_TextLine!
endlocal &rem schließt die hier geoeffnete setlocal-Instanz
) else (echo.)
) >>%_FncReplaseString_TmpFile%
move %_FncReplaseString_TmpFile% "%~3"
)
endlocal &rem schließt die oben geoeffnete setlocal-Instanz
exit /b 0
Für den Registryeditor (regedit.exe) stellt die Konvertierung kein Problem dar; er kann auch ANSI-formatierte .reg-Dateien importieren. Zu beachten bleibt aber, dass UNICODE über den Zeichensatz von ANSI hinaus weitere Zeichen darstellen kann. Auch wenn das eher selten vorkommt, könnten bei der Konvertierung von UNICODE zu ANSI Sonderzeichen verloren gehen. Testen lässt sich das, indem man die Quelldatei (hier test.reg ) zuvor von Hand in das ANSI-Format konvertiert und wieder zurückwandelt, um sie dann mit dem Original zu vergleichen:
c:\>type test.reg > test_ansi.reg
c:\>notepad test_ansi.reg
Über „Datei / Speichern unter“ im Feld „Codierung: Unicode“ auswählen und die Datei speichern.
c:\>comp test.reg test_ansi.reg
Die Dateien sollten identisch sein (andernfalls sind durch die Konvertierung Daten verloren gegangen).
Version 1.0.5: Es wurde eine Optimierung vorgenommen (
setlocal DisableDelayedExpansion
aus der for-Schleife entfernt und an den Anfang der Funktion gesetzt). -- NeonZero 09.10.2008Version 1.0.4: UNICODE-formatierte Dateien werden nun gemäß dem obigen Hinweis zuvor nach ANSI konvertiert. -- NeonZero 05.09.2008
Version 1.0.3: Der erste Teil der Funktion (String im uebergebenen Text austauschen und der Variablen (%1) zuweisen) wurde nun ebenfalls entsprechend der "!"-Problematik angepaßt. Dabei findet auch der unten stehende Hinweis von bastla und Biber betreffs endlocal & set "Var=%Var%" Anwendung. -- NeonZero 26.08.2008
Version 1.0.2: Der Quelltext wurde nach einem Hinweis von bastla nochmals angepaßt (endlocal hinzugefügt), um den unten beschriebenen Fehler zu beseitigen. -- NeonZero 22.08.2008
Version 1.0.1: Ein etwas aufbereiteter Quellcode, der das eingangs beschriebene Problem von Version 1.0.0 behebt. -- NeonZero 20.08.2008
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 94915
Url: https://administrator.de/contentid/94915
Ausgedruckt am: 24.11.2024 um 16:11 Uhr
8 Kommentare
Neuester Kommentar
Hallo NeonZero und willkommen im Forum!
Dass (temporäres) VBS für derlei (insbes für die Version mit 3 Parametern) wesentlich handlicher ist, willst Du ja sicherlich nicht von mir hören ...
... daher also doch ein Versuch in native Batch:
Zeile 5 ist übrigens ein Biber-Special von hier - passt, wie ich finde, aber auch irgendwie in diesen Thread ...
Grüße
bastla
Dass (temporäres) VBS für derlei (insbes für die Version mit 3 Parametern) wesentlich handlicher ist, willst Du ja sicherlich nicht von mir hören ...
... daher also doch ein Versuch in native Batch:
@echo off & setlocal
set from=all
set to=oh
set _x=Hallo!
echo\|set /P=%_x%_to_
setlocal enabledelayedexpansion
echo !_x:%from%=%to%!
setlocal disabledelayedexpansion
Grüße
bastla
Hallo NeonZero!
bringt erwartungsgemäß die Ausgabe:
Anders sieht das Ergebnis mit dieser Variante aus:
nämlich:
Vielleicht kannst Du das ja einmal brauchen ...
Grüße
bastla
endlocal hat die Eigenschaft, dass alle lokal gesetzten Variablen wieder zurückgesetzt werden; eigene Variablen sind danach leer (tatsächlich existieren sie nicht mehr).
Das ist ja der eigentliche Sinn der Verwendung von "setlocal" - allerdings gibt es einen netten Workaround (natürlich von Biber) für die "Mitnahme" zumindest einzelner Variablenwerte:@echo off
set "Test=Original"
setlocal
echo %Test%
set "Test=Kopie"
echo %Test%
endlocal
echo %Test%
Original
Kopie
Original
@echo off
set "Test=Original"
setlocal
echo %Test%
set "Test=Kopie"
echo %Test%
endlocal & set "Test=%Test%"
echo %Test%
Original
Kopie
Kopie
Grüße
bastla
Moin NeonZero,
auch wenn Du es sicherlich nicht so geplant hattest - herausgekommen ist nun definitiv ein HowTo, eine Anleitung.
Und dementsprechend stufe ich Deinen Beitrag auch hoch zum Tutorial.
Vielen Dank für Deine Ausarbeitung und vor allem für das Andere-Teilhaben-Lassen daran.
Abschließende Fussnote zur Verwendung von Setlocal/Endlocal: in dem GetAllDateTimeInfos.bat im Batch-Workshop III habe ich auch das Feature benötigt, dass Variablen "global" erhalten/gesetzt bleiben oder umgekehrt "globale" Variablen wieder gelöscht werden (/SET bzw. /UNSET-Option).
Auch das geht mit einem "Endlocal" an der richtigen Stelle - im Batch die Zeilen 16-20.
Grüße
Biber
auch wenn Du es sicherlich nicht so geplant hattest - herausgekommen ist nun definitiv ein HowTo, eine Anleitung.
Und dementsprechend stufe ich Deinen Beitrag auch hoch zum Tutorial.
Vielen Dank für Deine Ausarbeitung und vor allem für das Andere-Teilhaben-Lassen daran.
Abschließende Fussnote zur Verwendung von Setlocal/Endlocal: in dem GetAllDateTimeInfos.bat im Batch-Workshop III habe ich auch das Feature benötigt, dass Variablen "global" erhalten/gesetzt bleiben oder umgekehrt "globale" Variablen wieder gelöscht werden (/SET bzw. /UNSET-Option).
Auch das geht mit einem "Endlocal" an der richtigen Stelle - im Batch die Zeilen 16-20.
Grüße
Biber