Wie kann ich in einer Datei alles zwischen zwei Markierungen löschen?

Wie kann ich in einer Datei alles zwischen zwei Markierungen löschen?

Ich habe einen Text in einer Textdatei, in dem ich alles zwischen den Zeichenketten \{{[}und {]}\}löschen möchte - einschließlich dieser Zeichenketten selbst. Diese beiden Zeichenkettendürfenliegen sowohl auf verschiedenen als auch auf derselben Linie. In beiden Fällen Ander Zeile, in der der Anfang \{{[}liegt, möchte ich nicht, dass der Text davor, also links, gelöscht wird - und das gleiche gilt für den Text danach {]}\}.

Hier ein Beispiel: Gegeben sei eine Textdatei mit dem Inhalt

Bla Bla bla bla \{{[} more bla bla
even more bla bla bla bla. 

A lot of stuff might be here.

Bla bla {]}\} finally done.

Nonetheless, the \{{[} show {]}\} goes on.

Das Skript sollte eine weitere Textdatei mit dem Inhalt zurückgeben

Bla Bla bla bla  finally done.

Nonetheless, the  goes on.

Leider erwies sich diese scheinbar einfache Aufgabe als zu schwierig für mich sed. Ich bin zufrieden mitbeliebigLösung in jeder Sprache, solange ich nichts auf meinem Standard-Linux-Rechner installieren muss (C und etwas Java sind bereits installiert).

Antwort1

Mit perl:

perl -0777 -pe 's/\Q\{{[}\E.*?\Q{]}\}\E//gs'

Beachten Sie, dass die gesamte Eingabe vor der Verarbeitung in den Speicher geladen wird.

\Qsomething\Esoll somethingals Literalzeichenfolge und nicht als regulärer Ausdruck behandelt werden.

Um eine normale Datei direkt zu ändern, fügen Sie die -iOption hinzu:

perl -0777 -i -pe 's/\Q\{{[}\E.*?\Q{]}\}\E//gs' file.txt

Mit GNU awkoder mawk:

awk -v 'RS=\\\\\\{\\{\\[}|\\{\\]}\\\\}' -v ORS= NR%2

Dort definieren wir dieDatensatztrennzeichenals einer dieser Anfangs- oder Endmarker (nur gawkund mawkunterstützt RShier einen regulären Ausdruck). Aber wir müssen die Zeichen maskieren, die der reguläre Ausdrucksoperator sind (Backslash, {, [) und auch den Backslash noch einmal, weil er in Argumenten speziell ist (wird für Dinge wie , ... -vverwendet ), daher die zahlreichen Backslashes.\n\b

Dann müssen wir nur noch jeden anderen Datensatz ausdrucken. NR%2wäre 1(wahr) für jeden ungeraden Datensatz.

Bei beiden Lösungen gehen wir davon aus, dass die Markierungen übereinstimmen und diese Abschnitte nicht verschachtelt sind.

Um die Datei direkt zu ändern, fügen Sie mit neueren Versionen von GNU awkdie -i /usr/share/awk/inplace.awkOption ¹ hinzu.


¹verwende nicht-i inplaceas versucht zunächst, die Erweiterung (as oder ) aus dem aktuellen Arbeitsverzeichnis gawkzu laden , wo jemand Malware platziert haben könnte. Der Pfad der mit gelieferten Erweiterung kann je nach System unterschiedlich sein, siehe die Ausgabe voninplaceinplaceinplace.awkinplacegawkgawk 'BEGIN{print ENVIRON["AWKPATH"]}'

Antwort2

sed   -e:t -e'y/\n/ /;/\\{{\[}/!b'               \
      -e:N -e'/\\{{\[.*{\]}\\}/!N'               \
           -e's/\(\\{{\[}\).*\n/\1/;tN'          \
           -e'y/ /\n/;s/\\{{\[}/& /;ts'          \
      -e:s -e's/\(\[} [^ ]*\)\({\]}\\}\)/\1 \2/' \
      -ets -e's/..... [^ ]* .....//;s/ //g;bt'   \
<<""
#Bla Bla {]}\} bla bla \{{[} more bla bla
#even more bla bla bla bla. \{{[} 
#
#A lot of stuff might be here.
#hashes are for stupid syntax color only
#Bla bla {]}\} finally {]}\} done.
#
#Nonetheless, the \{{[} show {]}\} goes \{{[} show {]}\} on.

#Bla Bla {]}\} bla bla  finally {]}\} done.
#
#Nonetheless, the  goes  on.

Es gibt jedoch eine viel bessere Methode. Es gibt weitaus weniger Ersetzungen, und die, die vorgenommen werden, beziehen sich auf ein paar Zeichen gleichzeitig und nicht .*immer. Praktisch .*wird nur dann Ersetzungen verwendet, wenn der Musterraum zwischen den Leerzeichen gelöscht wird, wenn der erste vorkommende Anfang definitiv mit dem ersten folgenden Ende gepaart ist. In der übrigen Zeit wird sedeinfach Dso viel gelöscht wie nötig, um zum nächsten vorkommenden Trennzeichen zu gelangen. Don hat mir das beigebracht.

sed -etD -e:t -e'/\\{{\[}/!b'  \
    -e's//\n /;h;D'       -e:D \
    -e'/^}/{H;x;s/\n.*\n.//;}' \
    -ett    -e's/{\]}\\}/\n}/' \
    -e'/\n/!{$!N;s//& /;}' -eD \
<<""
#Bla Bla {]}\} bla bla \{{[} more bla bla
#even more bla bla bla bla. \{{[} 
#
#A lot of stuff might be here.
#hashes are for stupid syntax color only
#Bla bla {]}\} finally {]}\} done.
#
#Nonetheless, the \{{[} show {]}\} goes \{{[} show {]}\} on.

#Bla Bla {]}\} bla bla  finally {]}\} done.
#
#Nonetheless, the  goes  on.

Die \nEwline-Escapezeichen auf der rechten Seite müssen allerdings möglicherweise durch wörtliche, mit Backslash versehene Escape-Newline-Zeichen ersetzt werden.

Hier ist eine allgemeinere Version:

#!/usr/bin/sed -f
####replace everything between START and END
   #branch to :Kil if a successful substitution
   #has already occurred. this can only happen
   #if pattern space has been Deleted earlier
    t Kil
   #set a Ret :label so we can come back here
   #when we've cleared a START -> END occurrence
   #and check for another if need be
    :Ret
   #if no START, don't
    /START/!b
   #sigh. there is one. get to work. replace it
   #with a newline followed by an S and save
   #a copy then Delete up to our S marker.
    s||\
S|
    h;D
   #set the :Kil label. we'll come back here from now
   #on until we've definitely got END at the head of
   #pattern space.
    :Kil
   #do we? 
    /^E/{
       #if so, we'll append it to our earlier save
       #and slice out everything between the two newlines
       #we've managed to insert at just the right points        
        H;x
        s|\nS.*\nE||
    }
   #if we did just clear START -> END we should
   #branch back to :Ret and look for another START
    t Ret
   #pattern space didnt start w/ END, but is there even
   #one at all? if so replace it w/ a newline followed
   #by an E so we'll recognize it at the next :Kil
    s|END|\
E|
   #if that last was successful we'll have a newline
   #but if not it means we need to get the next line
   #if the last line we've got unmatched pairs and are
   #currently in a delete cycle anyway, but maybe we
   #should print up to our START marker in that case?
    /\n/!{
       #i guess so. now that i'm thinking about it
       #we'll swap into hold space, and Print it
        ${  x;P;d
        }
       #get next input line and add S after the delimiting
       #newline because we're still in START state. Delete
       #will handle everything up to our marker before we
       #branch back to :Kil at the top of the script
        N
        s||&S|
    }
   #now Delete will slice everything from head of pattern space
   #to the first occurring newline and loop back to top of script.
   #because we've definitely made successful substitutions if we
   #have a newline at all we'll test true and branch to :Kil 
   #to go again until we've definitely got ^E
    D

...ohne Kommentare...

#!/usr/bin/sed -f
    t Kil
    :Ret
    /START/!b
    s||\
S|
    h;D
    :Kil
    /^E/{
        H;x
        s|\nS.*\nE||
    }
    t Ret
    s|END|\
E|
    /\n/!{
        ${  x;P;d
        }
        N
        s||&S|
    }
    D

Ich habe die kommentierte Version in meine Zwischenablage kopiert und Folgendes getan:

{ xsel; echo; } >se.sed
chmod +x se.sed
./se.sed <se.sed

#!/usr/bin/sed -f
####replace everything between
   #branch to :Kil if a successful substitution
   #has already occurred. this can only happen
   #if pattern space has been Deleted earlier
    t Kil
   #set a Ret :label so we can come back here
   #when we've cleared a  occurrence
   #and check for another if need be
    :Ret
   #if no  at the head of
   #pattern space.
    :Kil
   #do we?
    /^E/{
       #if so, we'll append it to our earlier save
       #and slice out everything between the two newlines
       #we've managed to insert at just the right points
        H;x
        s|\nS.*\nE||
    }
   #if we did just clear  we should
   #branch back to :Ret and look for another , but is there even
   #one at all? if so replace it w/ a newline followed
   #by an E so we'll recognize it at the next :Kil
    s|END|\
E|
   #if that last was successful we'll have a newline
   #but if not it means we need to get the next line
   #if the last line we've got unmatched pairs and are
   #currently in a delete cycle anyway, but maybe we
   #should print up to our

Antwort3

Wenn Ihre Datei test.txt ist, können Sie Folgendes verwenden:

sed ':a;N;$!ba;s/\n/ /g' test.txt|sed 's/\\{{\[}.*{\]}\\}//' 

Der erste Sed entfernt alle Zeilenumbrüche, der zweite entfernt den Text innerhalb der Tags.

Ich weiß nicht, ob Sie eine allgemeinere Lösung brauchen

verwandte Informationen