umgekehrte Reihenfolge der Absätze in der Datei

8

Ich habe eine Datei mit Text in Absätzen (Zeilen mit Text durch eine oder mehrere leere Zeilen getrennt). Ich möchte die Reihenfolge der Absätze umkehren (dh der letzte Absatz wird der erste, ...), vorzugsweise mit sed.

Ich suche nach einem sed-Befehl, der mit einer Datei mit Absätzen und tacmit einer Datei mit Zeilen funktioniert.

Martin Vegter
quelle

Antworten:

6

Die Verwendung sedist nicht ganz so einfach wie von Joseph R .. Man könnte jedoch sagen:

sed '/./{H;d;};x;s/\n/={NL}=/g' inputfile | \
sed -e 's/^={NL}=//' -e '1!G;h;$!d' | \
sed G | sed 's/={NL}=/\'$'\n/g'

Bei einer Beispieleingabe:

Para 1 line 1
Para 1 line 2
Para 1 line 3

Para 2 line 1
Para 2 line 2
Para 2 line 3

Para 3 line 1
Para 3 line 2
Para 3 line 3

dies würde produzieren:

Para 3 line 1
Para 3 line 2
Para 3 line 3

Para 2 line 1
Para 2 line 2
Para 2 line 3

Para 1 line 1
Para 1 line 2
Para 1 line 3

Es ist erwähnenswert, dass diese Lösung (wie auch die alternative Perl-Lösung) eine leere Zeile am Ende der Eingabedatei benötigt, um wie erwartet zu funktionieren.

devnull
quelle
6

Diese Lösung verwendet beide tacund perlzum gleichzeitigen Lesen eines Absatzes. Es ist nicht erforderlich, die gesamte Datei in den Speicher einzulesen.

tac file | perl -00 -lpe '$_ = join "\n", reverse split /\n/'

Kehren Sie alle Zeilen der Datei um und kehren Sie dann für jeden umgekehrten Absatz die Zeilen um.

Glenn Jackman
quelle
Das sieht sehr elegant und effizient aus. Diese Lösung verdichtet jedoch auch mehrere leere (dh trennende) Zeilen zu einer
Martin Vegter
3

Es könnte einen Weg geben, dies zu tun sed, aber ich bezweifle, dass es einfach sein wird. So würde ich es in Perl machen:

perl -n00e 'push @paragraphs,$_; END{print for reverse @paragraphs}' your_file

Dies funktioniert, weil das Definieren des Trennzeichens für Eingabedatensätze als Nullzeichen ( -00) Perl anweist, im Absatzmodus zu arbeiten. Perls Definition eines Absatzes 1 entspricht genau Ihrer Definition.


1 Schauen Sie unter die ÜberschriftOther values for $/

Joseph R.
quelle
das funktioniert in der Tat. Das einzige kleine Problem ist, dass nicht mehrere leere Zeilen zwischen den Absätzen erhalten bleiben. Stattdessen werden alle Absätze durch genau eine leere Zeile getrennt.
Martin Vegter
1

Wenn Ihre Absätze immer durch eine einzelne leere Zeile getrennt sind:

sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n | \
sed 's/^\x03//;1s/\x03$//;1!G;h;$!d;$a\' | tr $'\003' \\n

Es ist ziemlich leicht zu sehen, wie es funktioniert, wenn man es in Stücke bricht und sed '/^$/s/^/\x02/' infiledann rennt sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\nund so weiter ...


Wenn Ihre Absätze durch eine oder mehrere Leerzeilen getrennt sind, z

Para 1 line 1
Para 1 line 2

Para 2 line 1


Para 3 line 1
Para 3 line 2

Para 4 line 1
Para 4 line 2



Para 5 line 1

und Sie möchten die Reihenfolge der Absätze umkehren, aber die Reihenfolge der "leeren Blöcke" beibehalten. Sie können die Datei zweimal lesen:
1. Umfassen Sie Absätze in einzelne Zeilen (Entfernen leerer Blöcke dazwischen) und kehren Sie sie um und
2. Umdrehen Sie die leeren Blöcke In einzelne Zeilen "indizieren" Sie die Anzahl der leeren Zeilen in jedem Block (und entfernen nicht leere Zeilen),
dann pastedie Ergebnisse und verarbeiten die Ausgabe, um neue Zeilen wiederherzustellen:

paste -d $'\004' <(sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n | \
sed -e '/^\x03$/d;s/^\x03//;s/\x03$//;1!G;h;$!d;$a\') \
<(sed -E '/^$/!d;//{:a;N;/^(\n){1,}$/ba;s/\n/\x02/g;s/(.*)\x02.*/\1/}' infile) \
| sed '$!s/\x04/\n/;$s/\x04$//' | tr $'\003\002' \\n\\n

welche Ausgänge:

Para 5 line 1

Para 4 line 1
Para 4 line 2


Para 3 line 1
Para 3 line 2

Para 2 line 1



Para 1 line 1
Para 1 line 2

Wenn Ihnen eine zusätzliche nachfolgende Zeile in der Ausgabe nichts ausmacht, können Sie die letzte löschen sed:

paste -d $'\n' <(sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n | \
sed -e '/^\x03$/d;s/^\x03//;s/\x03$//;1!G;h;$!d;$a\') \
<(sed -E '/^$/!d;//{:a;N;/^(\n){1,}$/ba;s/\n/\x02/g;s/(.*)\x02.*/\1/}' infile) | \
tr $'\003\002' \\n\\n

Diese gehen davon aus, dass die erste und die letzte Zeile nicht leer ist (und nicht \x02, \x03oder \x04im Eingang).

don_crissti
quelle
1

Sie können es mit einer einzelnen Instanz von tun sed; Keine Rohre notwendig. Da seddas Dokument nur einmal durchlaufen wird und sich der als Beginn der Ausgabe erforderliche Teil der Datei am Ende der Datei befindet, muss die gesamte Datei im Speicher sed(im Speicherbereich) gespeichert werden nicht gut skalieren. Aber es beantwortet die Frage genau:

:getpara
   ${
      s/$/\
/
      G
      s/\n\n$//
      q
   }
   N
   /\n$/!bgetpara
G
h
$!d
s/\n\n$//
q

Wenn kein nachfolgender Zeilenumbruch vorhanden ist, funktioniert dies weiterhin einwandfrei. Wenn es eine einzelne nachfolgende neue Zeile gibt, wird sie in der Ausgabe unterdrückt (dh es wird keine führende neue Zeile in der Ausgabe vorhanden sein). Wenn die Eingabe (zum Beispiel) 5 nachfolgende Zeilenumbrüche enthält, enthält die Ausgabe 4 führende Zeilenumbrüche.

Die Lücken zwischen den Absätzen bleiben erhalten.

Leerzeichen in einer ansonsten leeren Zeile werden NICHT als Absatzumbruch behandelt, aber das ist eine Funktion, kein Fehler. :) :)

Sie können dies auch als viel weniger lesbarer Einzeiler tun:

sed ':k;${;s/\(\(\n\).*\)$/\1\2/;G;s/\n\n$//;q;};N;/\n$/!bk;G;h;$!d;s/\n\n$//;q' inputfile

Dies funktioniert zwar nur mit GNU sed. (Beachten Sie die schwierige Verwendung von Backreferences für die Ausführung s/$/\n/. Ohne diese wäre es kein wörtlicher Einzeiler, da es einen Backslash-Newline enthalten würde.)

Platzhalter
quelle
Also schlürfen Sie die Datei, richtig? Es sieht so aus, als ob Sie das Ganze in den Laderaum stellen. w / G;h. Sie könnten etwas über Eingabebeschränkungen oder ähnliches erwähnen.
Mikeserv
Ich habe den Einzeiler nicht getestet, weil ich von meinem Mac aus arbeite und kein GNU zur sedHand habe, aber die Skriptversion bewahrt definitiv die Lücken zwischen den Absätzen. Ich habe es gerade auf Ihre Eingabe getestet. Haben Sie die Skriptversion getestet?
Wildcard
@mikeserv: Auf jeden Fall wahr. (Wird heute Abend aktualisiert.)
Wildcard
0
gem install facets

ruby -r facets/string \
     -e 'puts $stdin.read.strip.shatter(/\n\n+/).reverse.join("")' < file

Dies sollte Ihren sedAbsatzabstand beibehalten (während es besser lesbar ist als :)).

Amadan
quelle