Wie kann ich eine Datei (gut eingegebener Stream) so zuschneiden, dass nur die Zeilen vom ersten foo
bis zum letzten Auftreten des Musters angezeigt werden bar
?
Betrachten Sie zum Beispiel die folgende Eingabe:
A line
like
foo
this
foo
bar
something
something else
foo
bar
and
the
rest
Ich erwarte diese Ausgabe:
foo
this
foo
bar
something
something else
foo
bar
text-processing
sed
Rahmu
quelle
quelle
foo
und die letzte findenbar
und alles dazwischen drucken, wenn überhaupt. Bei einem Stream müssten Sie bis zum ersten lesenfoo
und alle nachfolgenden Zeilen im Speicher bis zum EOF puffern, wobei der Puffer jedes Mal geleert wird, wenn a angezeigtbar
wird. Dies kann bedeuten, dass der gesamte Stream im Speicher gepuffert wird.Antworten:
Die sed-Musterübereinstimmung
/first/,/second/
liest Zeilen nacheinander. Wenn eine Linie mit ihr übereinstimmt,/first/
merkt sie sich diese und freut sich auf die erste Übereinstimmung für das/second/
Muster. Gleichzeitig werden alle für dieses Muster angegebenen Aktivitäten angewendet. Danach beginnt der Vorgang immer wieder bis zum Ende der Datei.Das brauchen wir nicht. Wir müssen bis zum letzten Musterabgleich nachschlagen
/second/
. Deshalb bauen wir eine Konstruktion, die nur nach dem ersten Eintrag sucht/foo/
. Wenn gefunden,a
beginnt der Zyklus . Wir fügen dem Übereinstimmungspuffer eine neue Zeile hinzuN
und prüfen, ob sie mit dem Muster übereinstimmt/bar/
. Wenn dies der Fall ist, drucken wir es einfach aus und löschen den Übereinstimmungspuffer. Springen Sie mit janyway zum Beginn des Zyklus mitba
.Außerdem müssen wir das Zeilenumbruchsymbol nach der Pufferbereinigung mit löschen
/^\n/s/^\n//
. Ich bin mir sicher, dass es eine viel bessere Lösung gibt, die mir leider nicht in den Sinn gekommen ist.Hoffe alles ist klar.
quelle
sed
BSD zB Versionen sed (was auf Macs gefunden hat), Tags durch eine neue Zeile oder Ende der Zeichenkette gefolgt werden muß, so dass die folgenden zwicken notwendig sind:sed -n -e '/foo/{:a' -e 'N;/^\n/s/^\n//;/bar/{p;s/.*//;};ba' -e '};'
Das auch funktioniert auf GNU sed, so dass ich diese Modifikation (multiple denke-e
args Das Beenden eines Arg nach jedem Zweignamen ist eine gute tragbare Angewohnheit, wenn Sie Zweige in sed verwenden.Ich würde es mit einem kleinen Perl-Einzeiler machen.
ergibt
quelle
E
anstelle vone
und-00777
anstelle des$/
Bits verwenden (siehe Perlrun (1)). Was es verkürzen würde auf:perl -0777 -nE 'say /(foo.*bar)/s'
immer noch irgendwie lesbar.-0[octal]
das in meinem Workflow seinen Weg finden wird! Vielen Dank dafürHier ist eine GNU-Lösung mit zwei Durchgängen, die nicht viel Speicher benötigt:
Erläuterung
sed
Aufruf übergibt infile und findet das erste Auftretenfoo
und alle nachfolgenden Vorkommen vonbar
.sed
Skript mit zwei Aufrufen vonsed
und einem geformttr
. Die Ausgabe des drittensed
erfolgt[start_address],[end_address]p
ohne die Klammern.sed
Pässen wirdinfile
erneut ausgeführt, wobei die gefundenen Adressen und alles dazwischen gedruckt werden.quelle
Wenn die Eingabedatei bequem in den Speicher passt, halten Sie es einfach .
Wenn die Eingabedatei sehr groß ist, können Sie
csplit
sie beim erstenfoo
und bei jedem weiterenbar
Aufteilen in Teile zerlegen und dann zusammenfügen. Die Stücke werden genanntpiece-000000000
,piece-000000001
usw. ein Präfix Wählen Sie (hierpiece-
) , die mit anderen vorhandenen Dateien nicht kollidieren wird.(Auf Nicht-Linux-Systemen müssen Sie z. B. eine große Zahl in geschweiften Klammern verwenden
{999999999}
und die-k
Option übergeben. Diese Zahl ist die Anzahl derbar
Teile.)Sie können alle Teile mit zusammenbauen
cat piece-*
, aber dies gibt Ihnen alles nach dem erstenfoo
. Entfernen Sie also zuerst das letzte Stück. Da die von erstellten Dateinamencsplit
keine Sonderzeichen enthalten, können Sie sie ohne besondere Anführungszeichen für Zitate bearbeiten, z. B. mitoder äquivalent
Jetzt können Sie alle Teile verbinden und die temporären Dateien entfernen:
Wenn Sie die verketteten Teile entfernen möchten, um Speicherplatz zu sparen, führen Sie dies in einer Schleife aus:
quelle
Hier ist ein anderer Weg mit
sed
:Jede Zeile im
/foo/,$
Bereich (Zeilen, die!
nicht in diesem Bereich liegen, wirdd
entfernt) wird an denH
alten Platz angehängt . Nicht übereinstimmende Zeilenbar
werden dann gelöscht. Bei übereinstimmenden Zeilen wird der Musterbereich geleert,x
mit dem Haltebereich geändert und die führende leere Zeile im Musterbereich entfernt.Bei großen Eingaben und wenigen Vorkommen
bar
sollte dies (viel) schneller sein, als jede Zeile in den Musterraum zu ziehen und dann jedes Mal den Musterraum auf zu überprüfenbar
.Erklärt:
Sicher, wenn dies eine Datei ist (und in den Speicher passt), können Sie einfach Folgendes ausführen:
weil
ed
kann vorwärts und rückwärts suchen .Sie können sogar eine Befehlsausgabe in den Textpuffer lesen, wenn Ihre Shell die Prozessersetzung unterstützt:
oder wenn nicht, mit
gnu ed
:quelle
Verwenden eines beliebigen awk in einer beliebigen Shell auf einem beliebigen UNIX-System und ohne gleichzeitiges Lesen der gesamten Datei oder des Eingabestreams in den Speicher:
quelle
Grep könnte es auch tun (na ja, GNU grep):
Für die Eingabe aus dem Fragetext:
quelle