Mehrzeilige Datei-Shuffle

Mehrzeilige Datei-Shuffle

Ich habe eine Textdatei mit leeren Zeilen, die Textblöcke trennen. Ich möchte *NIX-Befehlszeilentools verwenden, um diese Datei unter Beibehaltung der Blockstruktur neu zu ordnen. Mit anderen Worten, in der Ausgabe möchte ich die geänderte Reihenfolge der Blöcke sehen; die Zeilen und ihre Reihenfolge innerhalb des Blocks bleiben gleich.

Beispiel für eine Eingabedatei:

line 1
line 2

line 10
line 20
line 30

line 100
line 200

Die Ausgabedatei (nach dem Mischen):

line 10
line 20
line 30

line 1
line 2

line 100
line 200

Natürlich sollte durch wiederholtes Ausführen eine andere Reihenfolge der Blöcke entstehen.

Die erste Zeile der Datei ist immer nicht leer. Es gibt keine doppelten Leerzeilen. Die letzte Zeile der Datei ist immer leer.

Ich habe ein sehr einfaches Python-Skript geschrieben, das alle Zeilen in einer Liste von Listen liest, sie mischt und ausgibt. Ich bin gespannt, ob ich das mit Standard-*NIX-Tools machen könnte.

Antwort1

POSIX-mäßig könnten Sie etwa Folgendes tun:

<file awk '
  BEGIN{srand(); n=rand()}
  {print n, NR, $0}
  !NF {n=rand()}
  END {if (NF) print n, NR+1, ""}' |
  sort -nk1 -k2 |
  cut -d' ' -f3-

Das heißt, stellen Sie jeder Zeile <a-random-number-that-changes-with-each-paragraph>die Zeilennummer voran, sortieren Sie dann numerisch nach der ersten und dann nach der zweiten Nummer, um die Zeilenreihenfolge in den Absätzen beizubehalten und diese zusätzlichen Nummern zu entfernen.

Möglicherweise möchten Sie sed '$d'die nachstehende Leerzeile mithilfe einer Pipe entfernen.

Beachten Sie, dass bei den meisten awkImplementierungen srand()die Unix-Epochenzeit zum Setzen des Pseudozufallszahlengenerators verwendet wird, so dass Sie möglicherweise das gleiche Ergebnis erhalten, wenn Sie den Generator zweimal in der gleichen Sekunde ausführen (einhistorischer Fehler jetzt in der POSIX-Spezifikation eingraviert, trotz meiner Bemühungen leider).

Antwort2

Mithilfe von GNU-Tools werden Absätze in durch NULs getrennte Gruppen unterteilt, neu gemischt und anschließend die NULs entfernt:

$ sed '1s/^/\n/; s/^$/\x00/' input | shuf -z | sed '1d; s/\x00//'
line 100
line 200

line 10
line 20
line 30

line 1
line 2

Alternativer Ansatz ohne Verwendung von NUL

Da nicht alle Tools NUL-Zeichen unterstützen, gibt es hier eine Alternative. Diese liest Absätze ein, ersetzt sie ~durch Zeilenumbrüche, mischt sie, konvertiert sie ~wieder in Zeilenumbrüche und zeigt die Ergebnisse an:

$ awk '{gsub(/\n/, "~")} 1' RS= input | shuf | awk '{gsub(/~/, "\n")} 1' ORS="\n\n"
line 10
line 20
line 30

line 100
line 200

line 1
line 2

Wenn Ihr Text möglicherweise enthält ~, verwenden Sie als temporären Zeilentrenner ein anderes Zeichen, das im Text nicht enthalten ist.

Antwort3

Mit Perl:

perl -MList::Util -00 -e 'chomp(my @a=<>); print join("\n\n", List::Util::shuffle @a) . "\n";' < input

Oder verteilt als Skript-Datei:

#!/usr/bin/perl
use List::Util 'shuffle';
local $/ = "";  ## paragraph mode
chomp(my @a = <>);
print join("\n\n", shuffle @a) . "\n";

verwandte Informationen