Linux: Zeichenkette in Textdatei automatisch suchen und ersetzen

shortrange

Banned
Registriert
Okt. 2013
Beiträge
626
Hallo zusammen,

ich versuche einem großen Textdokument verschiedene Zeichenketten durch andere Zeichenketten automatisiert ersetzen zu lassen.

Aktuell geht es um das Datumsformat bzw. die Schreibweise eines Datums. Das Datum ist aktuell noch in der Form "01. Januar" und soll in die Form "1. Januar" gebracht werden. Also die führende Null soll bei einstelligen Tagen entfernt werden.

Zuerst wollte ich mir mit grep einen Überblick machen:
Code:
grep -E "0[1-9]\.[[:blank:]](Januar|Februar|März|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember)" content.txt
Das funktioniert wunderbar, alles wird passend ausgegeben.

Nun geht es um das Ersetzen, wozu ich bisher versucht habe, folgenden sed-Befehl zu verwenden (hier am Beispiel des 3. Septembers):
Code:
sed s/03\.[[:blank:]]September/3\.[[:blank:]]September/g content.txt
Folgender Befehl wird ausgegeben:
Code:
3.[[:blank:]]September 2003
Das erste "blank" scheint zu funktionieren, sonst würde er ja die Stelle im Text nicht finden. Jedoch wird das zweite "blank" nicht als Leerzeichen eingebaut. Woran liegt das?

Was kann ich noch ändern, damit der Befehl nicht nur den 3. September ändert sondern jede führende Null bei der Datumsangabe?
 
Ersetze das zweite [[:blank:]] einfach durch ein Leerzeichen. Nur die Zeichenkette, nach der du suchst, ist ein regulärer Ausdruck (und kann daher Muster wie [[:blank:]] benutzen). Die Zeichenkette, die du stattdessen einfügen willst, ist ganz normaler Text. (Okay, fast - auch da können bestimmte Spezialbefehle vorkommen. Aber nicht [[:blank:]], denn das steht für eine ganze Gruppe von Zeichen, nicht nur das einfache Leerzeichen. sed könnte nicht wissen, welches Zeichen aus dieser Gruppe du genau meinst.)

Was kann ich noch ändern, damit der Befehl nicht nur den 3. September ändert sondern jede führende Null bei der Datumsangabe?

Dazu musst du die Tagesziffer und den Monatsnamen im Suchausdruck durch entsprechende Muster ersetzen: Ziffern kannst du mit [1-9] finden, die Monatsnamen musst du wohl alle auflisten. Wenn du diese Muster dann noch in Klammern setzt, dann kannst du sie beim Ersetzen wieder einfügen, indem du \n (n = Nummer des geklammerten Ausdrucks) schreibst.

Funktionieren sollte also so etwas wie:

Code:
sed s/0([1-9])\.[[:blank:]](Januar|Februar|und|so|weiter|bis|Dezember)/\1\. \2/g content.txt
 
Zuletzt bearbeitet:
NullPointer schrieb:
Funktionieren sollte also so etwas wie:

Code:
sed s/0([1-9])\.[[:blank:]](Januar|Februar|und|so|weiter|bis|Dezember)/\1\. \2/g content.txt
Danke für Deine ausführliche Antwort.

Ich habe jetzt folgendes ausprobiert:
Code:
sed s/0[1-9]\.[[:blank:]](Januar|Februar|März|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember)/\1\. \2/g content.txt
Jedoch erhalte ich leider die Fehlermeldung:
Code:
bash: Syntaxfehler beim unerwarteten Wort `('
Weißt Du weiter?
 
\1 und \2 sind sogenannte backreferences und beziehen sich auf mit rundklammern eingeklammerte abschnitte aus dem suchmuster. in NPs muster also z.b. "[0-9]" respektive "Januar|..." . wenn du willst, kannst du auch noch dein [[:blank:]] in klammern einsperren und dann das ersetzungsmuster in "\1\.\2\3" umaendern, damit behaeltst du, was auch immer [[:blank:]] jetzt gerade captured.
 
Dir fehlen jetzt die Klammern um die Zahl, außerdem hat die Klammer in der Bash auch eine Bedeutung. Daher müssen wir den Parameter mit Single Quotes einschließen:

Code:
 sed 's/0([1-9])\.[[:blank:]](Januar|Februar|März|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember)/\1\. \2/g' content.txt

Danach wirst du wahrscheinlich eine Meldung erhalten, dass die Backreference (hab gerade kein deutsches System zur Hand) \2 ungültig ist. Bei auf Anhieb komisch klingenden Fehlermeldungen zahlt es sich öfter mal aus, den Parameter -r mitzugeben:

-r, --regexp-extended
use extended regular expressions in the script.

Dadurch erhältst du:

Code:
 sed -r 's/0([1-9])\.[[:blank:]](Januar|Februar|März|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember)/\1\. \2/g' content.txt

Ich würde -r jetzt nicht pauschal bei jeder Anwendung von sed nutzen, hat ja vermutlich einen Grund, dass das nicht standardmäßig so ist.

Und damit sollte das funktionieren. Wenn dir die Ausgabe gefällt kannst du den Schalter -i noch mit angeben, damit ersetzt du die Datei in place ;)
 
Danke für Eure Antworten.
Linus9000 schrieb:
Dir fehlen jetzt die Klammern um die Zahl, außerdem hat die Klammer in der Bash auch eine Bedeutung. Daher müssen wir den Parameter mit Single Quotes einschließen:

Code:
 sed 's/0([1-9])\.[[:blank:]](Januar|Februar|März|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember)/\1\. \2/g' content.txt

Danach wirst du wahrscheinlich eine Meldung erhalten, dass die Backreference (hab gerade kein deutsches System zur Hand) \2 ungültig ist.
Richtig, ich erhalte folgende Fehlermeldung:
Code:
sed: -e Ausdruck #1, Zeichen 118: Ungültiger Verweis \2 im rechten Teil (`RHS') des `s'-Befehls


Linus9000 schrieb:
Bei auf Anhieb komisch klingenden Fehlermeldungen zahlt es sich öfter mal aus, den Parameter -r mitzugeben:



Dadurch erhältst du:

Code:
 sed -r 's/0([1-9])\.[[:blank:]](Januar|Februar|März|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember)/\1\. \2/g' content.txt

Ich würde -r jetzt nicht pauschal bei jeder Anwendung von sed nutzen, hat ja vermutlich einen Grund, dass das nicht standardmäßig so ist.

Und damit sollte das funktionieren. Wenn dir die Ausgabe gefällt kannst du den Schalter -i noch mit angeben, damit ersetzt du die Datei in place ;)
Das funktioniert, danke!

Eine andere Frage noch:
Sed hat ja zwei Bereiche. Einmal den "Such"-Bereich und einmal den "Ersetzen durch"-Bereich.

In dem Code oben ist es so, dass wir im vorderen Such-Bereich den Ausdruck "[[:blank:]]" verwenden, während wir im hinteren Ersetzen-durch-Bereich ein normales Leerzeichen verwenden. Ich habe mich gefragt, warum das so ist.
Ursprünglich habe ich [[:blank:]] wegen grep verwendet (siehe Post #1), welches mit einem Leerzeichen nicht zurechtgekommen ist. Meine Quelle dafür ist https://wiki.ubuntuusers.de/grep/#Listen-bracket-expressions .

Ich habe Sed jetzt nochmal ohne [[:blank:]] ausprobiert, also vorne und hinten ein Leerzeichen. Es hat ebenfalls funktioniert und die führenden Nullen wurden entfernt.

Hier schreibt @NullPointer noch etwas zu [[:blank:]]
NullPointer schrieb:
Ersetze das zweite [[:blank:]] einfach durch ein Leerzeichen. Nur die Zeichenkette, nach der du suchst, ist ein regulärer Ausdruck (und kann daher Muster wie [[:blank:]] benutzen). Die Zeichenkette, die du stattdessen einfügen willst, ist ganz normaler Text. (Okay, fast - auch da können bestimmte Spezialbefehle vorkommen. Aber nicht [[:blank:]], denn das steht für eine ganze Gruppe von Zeichen, nicht nur das einfache Leerzeichen. sed könnte nicht wissen, welches Zeichen aus dieser Gruppe du genau meinst.)
Nun zur Frage: Warum kommt sed mit einem Leerzeichen zurecht (auch in dem Such-Bereich), während man bei grep [[:blank:]] verwenden muss?
 
Update: grep kommt auch mit Leerzeichen zurecht.

Die Befehle
Code:
grep -cE "0[1-9]\.[[:blank:]](Januar|Februar|März|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember)" content.txt
und
Code:
grep -cE "0[1-9]\. (Januar|Februar|März|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember)" content.txt

geben beide die gleiche Anzahl an Treffern aus.


Update 2: Zum Test habe ich gerade nochmal eine andere Datei mit obigen Befehlen durchsucht.

Mit normalem Leerzeichen werden über -c 48.207 Treffer gefunden, mit [[:blank:]] werden 48.214 Treffer gefunden. Die Abweichung beträgt 0,014518605 Prozent. Wahrscheinlich ist bei manchen Datumsangaben zwischen Tag und Monat ein Absatz, zwei Leerzeichen o.ä.
 
Zuletzt bearbeitet:
Ne, die blank character class findet nur ein Zeichen, außer du fügst einen multiplier wie + oder * ein. Es gibt aber mehrere Arten von derartigen whitespaces, z.b. tabulator, non-breaking space, etc.
 
Zuletzt bearbeitet:
Zurück
Oben