Top-Themen

AppleEntwicklungHardwareInternetLinuxMicrosoftMultimediaNetzwerkeOff TopicSicherheitSonstige SystemeVirtualisierungWeiterbildungZusammenarbeit

Aktuelle Themen

Administrator.de FeedbackApache ServerAppleAssemblerAudioAusbildungAuslandBackupBasicBatch & ShellBenchmarksBibliotheken & ToolkitsBlogsCloud-DiensteClusterCMSCPU, RAM, MainboardsCSSC und C++DatenbankenDatenschutzDebianDigitiales FernsehenDNSDrucker und ScannerDSL, VDSLE-BooksE-BusinessE-MailEntwicklungErkennung und -AbwehrExchange ServerFestplatten, SSD, RaidFirewallFlatratesGoogle AndroidGrafikGrafikkarten & MonitoreGroupwareHardwareHosting & HousingHTMLHumor (lol)Hyper-VIconsIDE & EditorenInformationsdiensteInstallationInstant MessagingInternetInternet DomäneniOSISDN & AnaloganschlüsseiTunesJavaJavaScriptKiXtartKVMLAN, WAN, WirelessLinuxLinux DesktopLinux NetzwerkLinux ToolsLinux UserverwaltungLizenzierungMac OS XMicrosoftMicrosoft OfficeMikroTik RouterOSMonitoringMultimediaMultimedia & ZubehörNetzwerkeNetzwerkgrundlagenNetzwerkmanagementNetzwerkprotokolleNotebook & ZubehörNovell NetwareOff TopicOpenOffice, LibreOfficeOutlook & MailPapierkorbPascal und DelphiPeripheriegerätePerlPHPPythonRechtliche FragenRedHat, CentOS, FedoraRouter & RoutingSambaSAN, NAS, DASSchriftartenSchulung & TrainingSEOServerServer-HardwareSicherheitSicherheits-ToolsSicherheitsgrundlagenSolarisSonstige SystemeSoziale NetzwerkeSpeicherkartenStudentenjobs & PraktikumSuche ProjektpartnerSuseSwitche und HubsTipps & TricksTK-Netze & GeräteUbuntuUMTS, EDGE & GPRSUtilitiesVB for ApplicationsVerschlüsselung & ZertifikateVideo & StreamingViren und TrojanerVirtualisierungVisual StudioVmwareVoice over IPWebbrowserWebentwicklungWeiterbildungWindows 7Windows 8Windows 10Windows InstallationWindows MobileWindows NetzwerkWindows ServerWindows SystemdateienWindows ToolsWindows UpdateWindows UserverwaltungWindows VistaWindows XPXenserverXMLZusammenarbeit

Batch - setlocal EnableDelayedExpansion - ersetzen von Text, der ein Ausrufezeichen enthält

Anleitung Entwicklung Batch & Shell

Mitglied: NeonZero

NeonZero (Level 1) - Jetzt verbinden

20.08.2008, aktualisiert 18.10.2012, 29051 Aufrufe, 8 Kommentare

a) Batch-Variable: !-Zeichen durch ^^! ersetzen (wurde verworfen)
b) zwischen den setlocal-Modi DisableDelayedExpansion und EnableDelayedExpansion wechseln
01.
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.
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:

01.
@echo off & setlocal EnableDelayedExpansion 
02.
 
03.
call :FncReplaseString "foo" "bar" "d:\tmp\test.txt" 
04.
goto :EOF 
05.
 
06.
:FncReplaseString  ::Version 1.0.0 
07.
::Parameters:   <FromString> <ToString> <FileName> 
08.
::        or:   <VarNameToReturnReplasedText> <FromString> <ToString> <TextToReplase> 
09.
  if not "%~4" == "" ( ::String im übergebenen Text austauschen 
10.
    set _FncReplaseString_Text=%~4 
11.
    set %~1=!_FncReplaseString_Text:%~2=%~3! 
12.
    rem Alternativ dazu: call set %~1=%%_FncReplaseString_Text:%~2=%~3%% 
13.
  ) else if exist "%~3" ( ::String in einer Text-Datei austauschen 
14.
    set _FncReplaseString_TmpFile=%TMP%\%~n0_out%RANDOM%.tmp 
15.
    for /F "tokens=1* delims=:" %%a in ('findstr /n $ %3') do ( 
16.
      if not "%%b" == "" ( 
17.
        set _FncReplaseString_TextLine=%%b 
18.
        set _FncReplaseString_TextLine=!_FncReplaseString_TextLine:%~1=%~2! 
19.
        echo.!_FncReplaseString_TextLine! 
20.
      ) else (echo.) 
21.
    ) >>!_FncReplaseString_TmpFile! 
22.
    move "!_FncReplaseString_TmpFile!" "%~3" 
23.
24.
 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:

01.
@echo off & setlocal EnableDelayedExpansion 
02.
set _x=Hallo^^! 
03.
echo _x=!_x! 
04.
------------------ 
05.
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

01.
@echo off 
02.
 
03.
call :FncReplaseString "foo" "bar" "d:\tmp\test.txt" 
04.
goto :EOF 
05.
 
06.
:FncReplaseString  ::Version 1.0.5 
07.
::Parameters:   <FromString> <ToString> <FileName> 
08.
::        or:   <VarNameToReturnReplasedText> <FromString> <ToString> <TextToReplase> 
09.
::Note:         "="-Characters are not allowed in FromString 
10.
::Example:      call :FncReplaseString _Var "foo" "bar" "the foo"     (_Var="the bar") 
11.
::              call :FncReplaseString "foo" "bar" "d:\text.txt"      (replase string in text-File) 
12.
  setlocal DisableDelayedExpansion &::damit bei der Wertzuweisung keine Ausrufezeichen verloren gehen 
13.
  if "%~4" == "" goto :LEB_FncReplaseString_File 
14.
  ::String im uebergebenen Text austauschen und der Variablen (%1) zuweisen 
15.
    set "_FncReplaseString_Text=%~4" 
16.
    setlocal EnableDelayedExpansion  &::ermoeglicht die folgende Syntax 
17.
    set _FncReplaseString_Text=!_FncReplaseString_Text:%~2=%~3! 
18.
      rem Alternativ dazu: call set %~1=%%_FncReplaseString_Text:%~2=%~3%% 
19.
    ::die beiden oben und hier geoeffneten setlocal-Instanzen schließen & Variable uebernehmen: 
20.
    endlocal & set "_FncReplaseString_Text=%_FncReplaseString_Text%" 
21.
    endlocal & set "%~1=%_FncReplaseString_Text%" 
22.
   exit /b 0 
23.
    
24.
  :LEB_FncReplaseString_File 
25.
  set _FncReplaseString_TmpFile=%TMP%\%~n0_out%RANDOM%.tmp 
26.
  if exist "%~3" ( ::String in einer Text-Datei austauschen 
27.
    for /F "tokens=1* delims=:" %%a in ('type %3 ^| findstr /n $') do ( 
28.
    ::type konvertiert UC nach ANSI; findstr verhindert, dass Leerzeilen verloren gehen 
29.
      if not "%%b" == "" ( 
30.
        set _FncReplaseString_TextLine=%%b 
31.
        setlocal EnableDelayedExpansion  &::ermoeglicht die folgende Syntax 
32.
        set _FncReplaseString_TextLine=!_FncReplaseString_TextLine:%~1=%~2! 
33.
        echo.!_FncReplaseString_TextLine! 
34.
        endlocal &rem schließt die hier geoeffnete setlocal-Instanz 
35.
      ) else (echo.) 
36.
    ) >>%_FncReplaseString_TmpFile% 
37.
    move %_FncReplaseString_TmpFile% "%~3" 
38.
39.
  endlocal &rem schließt die oben geoeffnete setlocal-Instanz 
40.
 exit /b 0
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:
01.
c:\>type test.reg > test_ansi.reg 
02.
c:\>notepad test_ansi.reg 
03.
  Über „Datei / Speichern unter“ im Feld „Codierung: Unicode“ auswählen und die Datei speichern. 
04.
c:\>comp test.reg test_ansi.reg 
05.
  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.2008

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
Mitglied: bastla
20.08.2008, aktualisiert 18.10.2012
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:
01.
@echo off & setlocal 
02.
set from=all 
03.
set to=oh 
04.
set _x=Hallo! 
05.
echo\|set /P=%_x%_to_ 
06.
setlocal enabledelayedexpansion 
07.
echo !_x:%from%=%to%! 
08.
setlocal disabledelayedexpansion
Zeile 5 ist übrigens ein Biber-Special von hier - passt, wie ich finde, aber auch irgendwie in diesen Thread ...

Grüße
bastla
Bitte warten ..
Mitglied: NeonZero
20.08.2008 um 22:58 Uhr
Danke, bastla, für die schnelle Antwort.

Die Lösung: setlocal DisableDelayedExpansion muss gesetzt sein _bevor_ die Wertzuweisung per set _FncReplaseString_TextLine=%%b erfolgt. Andernfalls befindet sich in _FncReplaseString_TextLine bereits kein Ausrufezeichen mehr. Meine falsche Annahme war, dass die Eliminierung des Zeichens erst bei der Ausgabe erfolgt. Bei meiner Suche nach einer Lösung hatte ich so die ganze Zeit versucht, jedes Aufkommen eines „!“-Zeichens durch „^^!“ zu ersetzen, in einem String, der schon längst kein Ausrufezeichen mehr enthielt.

Im einleitenden Beitrag habe ich nun den kompletten etwas aufbereiteten Quelltext als Nachtrag angehängt.

Bye, nz
Bitte warten ..
Mitglied: NeonZero
22.08.2008 um 18:32 Uhr
Zu früh gefreut: Die zweite Version der Funktion kann lediglich Textdateien bearbeiten, die maximal 16 Zeilen enthält. Dann kommt es zur folgenden Fehlerausschrift:

                           Maximale Rekursionstiefe für SETLOCAL erreicht.

Ich habe schon alles mir Mögliche probiert, aber es scheint nichts um einen ständigen Wechsel zwischen setlocal DisableDelayedExpansion und setlocal EnableDelayedExpansion vorbeizuführen.

Bleibe man komplett bei setlocal DisableDelayedExpansion, wäre
     set _FncReplaseString_TextLine=!_FncReplaseString_TextLine:%~1=%~2!
nicht mehr erlaubt. Stattdessen kann man
     call set _FncReplaseString_TextLine=%%_FncReplaseString_TextLine:%~1=%~2%%
verwenden. Nur dass eben dieser Konstrukt nicht mit den üblichen Sonderzeichen der DOS-Konsole zurechtkommt. „Hallo!“ bliebe zwar erhalten (inkl. „!“-Zeichen), aber eine Zeile die z.B. „x>y“ enthält, würde dann auf „x“ verkürzt. Damit käme man vom Regen in die Traufe.

Das mit dem Ausrufezeichen bei DelayedExpansion ist schon sehr ärgerlich. Auch Programmierer dürfen Fehler machen. Aber warum wird dieser gottverda^W Mis^W, äh, meine, warum wird das in all den Jahren nicht gefixt?

Womöglich hat ja einer von euch einen zündenden Gedanken für einen anderen Ansatz (?).

Bye, nz

PS: Bastla hat es oben richtig erkannt: Ich suche eine Lösung für Batch, nicht für vbs.
Bitte warten ..
Mitglied: bastla
22.08.2008 um 18:38 Uhr
Hallo NeonZero!

Vielleicht lässt sich ja an passender Stelle ein "endlocal" einstreuen ...

Grüße
bastla
Bitte warten ..
Mitglied: NeonZero
22.08.2008 um 19:11 Uhr
Hallo bastla. Schön von Dir 'zu hören'.

endlocal hat die Eigenschaft, dass alle lokal gesetzten Variablen wieder zurückgesetzt werden; eigene Variablen sind danach leer (tatsächlich existieren sie nicht mehr). Deshalb kam die Verwendung für mich nicht in Frage.

Davon war ich so überzeugt, dass ich es in diesem Fall nicht einmal ausprobiert hatte. Aber jetzt kommt’s: Diese Folgen gibt es hier nicht. Ich habe (noch) keine Ahnung, warum das so ist. Davon unabhängig: Du hast es! Big thanks! Den als Nachtrag angehängten Quelltext (siehe einleitenden Beitrag) habe ich entsprechend angepasst. Ich denke, das war’s.

Bye, nz
Bitte warten ..
Mitglied: bastla
22.08.2008 um 19:42 Uhr
Hallo NeonZero!

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:
01.
@echo off 
02.
set "Test=Original" 
03.
setlocal 
04.
echo %Test% 
05.
set "Test=Kopie" 
06.
echo %Test% 
07.
endlocal 
08.
echo %Test%
bringt erwartungsgemäß die Ausgabe:
01.
Original 
02.
Kopie 
03.
Original
Anders sieht das Ergebnis mit dieser Variante aus:
01.
@echo off 
02.
set "Test=Original" 
03.
setlocal 
04.
echo %Test% 
05.
set "Test=Kopie" 
06.
echo %Test% 
07.
endlocal & set "Test=%Test%" 
08.
echo %Test%
nämlich:
01.
Original 
02.
Kopie 
03.
Kopie
Vielleicht kannst Du das ja einmal brauchen ...

Grüße
bastla
Bitte warten ..
Mitglied: NeonZero
22.08.2008 um 21:36 Uhr
Ein nützlicher Hinweis. Danke. Ich habe diese Variante gleich für den ersten Teil der Funktion adoptiert (betrifft String im uebergebenen Text austauschen und der Variablen zuweisen). Das musste ja auch noch entsprechend der „!“-Problematik angepasst werden.

Die Möglichkeiten von setlocal hatte ich bislang nur zum Teil erfasst. Mir war nicht klar, dass man mehrere setlocal-Instanzen verschachteln kann (eine neue Instanz legt sich um die darunter liegende ältere Instanz – ähnlich den Schichten einer Zwiebel). Genau das war es, was mir die Fehlermeldung "Maximale Rekursionstiefe für SETLOCAL erreicht" versucht hat zu sagen.

endlocal wirkt demzufolge auch nicht global, sondern beendet immer nur die äußere setlocal-Instanz. Daher hatte der Aufruf keinen Einfluss auf meine „darunter liegende“ Umgebung, deren Variablen in einer weiter innen liegenden setlocal-Instanz angelegt wurden. Diese Variablen existierten in den äußeren Schichten nur als Kopie, deren Änderung keinen Einfluss auf das Original hat. Beendet man die äußeren Schichten, kommt irgendwann wieder das Original mit dem ursprünglichen Inhalt zum Vorschein. Neu war für mich die Erkenntnis, dass es mehrere dieser Schichten geben kann.

Aber: setlocal EnableDelayedExpansion genauso wie setlocal DisableDelayedExpansion ändert nicht einfach so den Modus der aktuellen setlocal-Instanz. Es wird dabei immer eine neue Instanz angelegt, in der der veränderte Modus wirkt. Tatsächlich hat das keinen Einfluss auf die ursprüngliche Instanz. Der oft zu sehende einleitende Befehl „@echo off & setlocal & setlocal EnableDelayedExpansion“ ist demzufolge um den ersten setlocal-Befehl überflüssig, denn es werden sonst zwei setlocal-Instanzen geöffnet.

Fazit: Zusammengenommen ergibt sich der Grundsatz, dass auf jede setlocal-Instanz, die in einer Funktion angelegt wird (und sei es nur zum Wechsel des Modus), ein endlocal folgen muss.

Das war dann auch die Lösung für das Problem.

Bye, nz
Bitte warten ..
Mitglied: Biber
03.09.2008, aktualisiert 18.10.2012
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
Bitte warten ..
Neuester Wissensbeitrag
Internet

Unbemerkt - Telekom Netzumschaltung! - BNG - Broadband Network Gateway

(3)

Erfahrungsbericht von ashnod zum Thema Internet ...

Ähnliche Inhalte
Batch & Shell
gelöst Suchen und Ersetzen mehrerer Suchbegriffe per Batch (4)

Frage von makroll10 zum Thema Batch & Shell ...

Batch & Shell
Powershell - In Textdatei suchen und ersetzen (3)

Frage von Raaja89 zum Thema Batch & Shell ...

Heiß diskutierte Inhalte
Switche und Hubs
Trunk für 2xCisco Switch. Wo liegt der Fehler? (17)

Frage von JayyyH zum Thema Switche und Hubs ...

Windows Server
Outlook Verbindungsversuch mit Exchange (15)

Frage von xbast1x zum Thema Windows Server ...

DSL, VDSL
DSL-Signal bewerten (14)

Frage von SarekHL zum Thema DSL, VDSL ...