Abschnitte aus Text löschen mit sed

LazyJoe

Erdapfel
Registriert
15.08.11
Beiträge
3
Moin moin!

folgendes:
Habe aus Safari's History.plist folgenden Abschnitt:
Code:
    <dict>           
 <key></key>
            <string>http://www.macuser.de/forum/f30/safari-alle-cache-544226/</string>
            <key>D</key>
            <array>
                <integer>1</integer>
            </array>
            <key>lastVisitedDate</key>
            <string>335083562.5</string>
            <key>title</key>
            <string>Safari: Alle Cache Dateien via Script löschen</string>
            <key>visitCount</key>
            <integer>1</integer>
        </dict>

In der kompletten Datei sind natürlich noch mehr solche Abschnitte, die alle den selben Aufbau haben.

Ich möchte jetzt per Script komplette Abschnitte löschen, in denen bestimmte Keywords vorkommen. Hier als Beispiel macuser.de
Nach einiger Suche bin ich auf den Befehl sed gekommen und habe nun vor, alles, was zwischen
Code:
    <dict>            <key></key>
            <string>http://www.macuser.de/
und dem darauffolgenden </dict> kommt, zu löschen.

Zuerst habe ich also die History.plist woanders hinkopiert, und mit
Code:
plutil -convert xml1
so umgewandelt, dass ich sie mit vi lesen kann.

Anschließend soll mit folgendem Befehl der entsprechende Bereich gelöscht werden, was allerdings nicht funktioniert:
Code:
sed 's!<dict><key><\/key><string>http:\/\/www.macuser.de\/*<\/dict>!!g' History.plist >HistoryNeu.plist
Ich bekomme keine Fehlermeldung, der Inhalt der alten Datei wird in HistoryNeu.plist ausgegeben, aber der entsprechende Bereich ist immernoch vorhanden.

Liegt das daran, dass mein Suchstring über mehrere Zeilen verteilt ist? Muss ich außer den / sonst noch was escapen? Funktioniert der * als Platzhalter nicht? Oder gibt's viel einfachere oder schlankere Lösungen?

Sinn und Unsinn der Aktion lassen wir mal außen vor, dient auch zum Großteil zu Übungszwecken :D

Vielleicht hat ja jemand eine Idee.

Schöne Grüße!
Max
 

Rastafari

deaktivierter Benutzer
Registriert
10.03.05
Beiträge
18.150
1) Der Stern ist kein Platzhalter, sondern eine beliebige Wiederholung des vorangegangenen Zeichens (Null mal oder öfter.)
Der Platzhalter für jedes Zeichen ist der Punkt. Das Muster für eine beliebige Zeichenfolge ist also:
Code:
.*

2) sed sucht/erkennt natürlich auch Whitespace. Du hast schlicht und ergreifend keinen Treffer zu erwarten wenn du den nicht vorher entfernst.

3) Den Schrägstrich escapen? Warum das denn? Den Backslash, ja. Den Slash? Nein.
 

LazyJoe

Erdapfel
Registriert
15.08.11
Beiträge
3
zu 1)
Danke, dann mach ich also den Punkt noch davor.

zu 2)
Es gibt auch nicht zufällig 'ne Möglichkeit, Whitespace bei der Suche zu "überspringen", oder?

zu 3)
Auf die Idee kam ich, weil zum Trennen von Suchbegriff und Ersatzwort ja ein / benutzt wird, der leider auch in meinem Suchbegriff vorkommt. Ohne den / zu escapen kam es zu Fehlermeldungen.
 

Rastafari

deaktivierter Benutzer
Registriert
10.03.05
Beiträge
18.150
Es gibt auch nicht zufällig 'ne Möglichkeit, Whitespace bei der Suche zu "überspringen"
Nimm ihn ins Suchmuster auf. zB:
Code:
s,\(</[^>]*>\)[ [B][COLOR=#ff0000]T[/COLOR][/B]]*\(<[^/>]*>\),\1\2,g
Das rote T steht hier (damit mans sieht...) für einen Tab.
Dieser Subst entfernt alle Folgen von Leerzeichen und/oder Tabs zwischen normal schliessenden und öffnenden Tags.
(Berücksichtigt aber noch immer keine Newlines. "sed" ist zum zeilenweisen arbeiten gut, für Absatz- oder Blockbasierten Text... Phhuh!)
Zum besseren Verständnis hier noch ein paar mal die gleiche Zeile mit "Syntax Highlights für Arme". Jeweils zusammengehöriges, das ein Strukturelement bildet, ist hervorgehoben:
Code:
s,[COLOR=#ff0000]\([/COLOR]</[^>]*>[COLOR=#ff0000]\)[/COLOR][ [B]T[/B]]*\(<[^/>]*>\),[COLOR=#ff0000]\1[/COLOR]\2,g

s,\(</[^>]*>\)[ [B]T[/B]]*[COLOR=#ff0000]\([/COLOR]<[^/>]*>[COLOR=#ff0000]\)[/COLOR],\1[COLOR=#ff0000]\2[/COLOR],g

s,\(</[^>]*>\)[COLOR=#ff0000][ [B]T[/B]]*[/COLOR]\(<[^/>]*>\),\1\2,g

s,\([COLOR=#00ff00]</[/COLOR][COLOR=#ff0000][^>]*[/COLOR][COLOR=#00ff00]>[/COLOR]\)[ [B]T[/B]]*\([COLOR=#00ff00]<[/COLOR][COLOR=#ff0000][^/>]*[/COLOR][COLOR=#00ff00]>[/COLOR]\),\1\2,g

weil zum Trennen von Suchbegriff und Ersatzwort ja ein / benutzt wird
Nein. Du hast dafür das ! definiert. Das erste Zeichen nach dem Befehl s gilt, und du kannst dir (fast) alles aussuchen was im Muster ansonsten nicht vorkommt. Jeder Ausdruck kann sein eigenes Trennzeichen verwenden, wie es grade gut passt. Wie du oben siehst, finde ich das Komma sehr praktisch und viel besser lesbar.
Hier noch mal die gleiche Zeile mit beliebigen anders definierten Trennern (ist alles das gleiche):
Code:
s_\(</[^>]*>\)[ [B][COLOR=#FF0000]T[/COLOR][/B]]*\(<[^/>]*>\)_\1\2_g

sG\(</[^>]*>\)[ [B][COLOR=#FF0000]T[/COLOR][/B]]*\(<[^/>]*>\)G\1\2Gg

s-\(</[^>]*>\)[ [B][COLOR=#FF0000]T[/COLOR][/B]]*\(<[^/>]*>\)-\1\2-g
 
Zuletzt bearbeitet:

knifte

Jonagold
Registriert
16.08.10
Beiträge
20
Hallo.

Mit Perl lässt sich das lösen.
Der Ersetzungsoperator von Perl funktioniert auch über Zeilenumbrüche hinweg, so dass man auch ganze Blöcke bearbeiten kann.

Beispiel

testfile:

Code:
Hier steht was
und noch was
    <dict>           
 <key></key>
            <string>http://www.macuser.de/forum/f30/safari-alle-cache-544226/</string>
            <key>D</key>
            <array>
                <integer>1</integer>
            </array>
            <key>lastVisitedDate</key>
            <string>335083562.5</string>
            <key>title</key>
            <string>Safari: Alle Cache Dateien via Script löschen</string>
            <key>visitCount</key>
            <integer>1</integer>
</dict> Test 
Ende

Hier der Perl EInzeiler:
Code:
cat testfile | perl -e '@a = <STDIN> ; $s = "@a" ; $s =~ s#<dict>.*<key></key>.*<string>http://www.macuser.de.*</dict>##gs ; print $s'

und das Ergebnis:
Code:
Hier steht was 
und noch was
      Test 
 Ende

Geht evtl. auch eleganter aber scheint zu funktionieren ;)
Vielleicht hilft das ja weiter.

Gruß,
René
 

LazyJoe

Erdapfel
Registriert
15.08.11
Beiträge
3
Oh, das klingt deutlich komplizierter als angenommen.
Muss mich jetzt leider bis zum Ende der Woche in die Internet-freie Zone verabschieden, aber danke euch beiden schonmal für die Erklärungen bzw. das Perl-Script!

Denke mal ich werde das dann mit Perl lösen! :)