Wie kann ich die Zeilen einer Textdatei in der Unix-Befehlszeile oder in einem Shell-Skript mischen?

Ich möchte die Zeilen einer Textdatei zufällig mischen und eine neue Datei erstellen. Die Datei kann mehrere tausend Zeilen enthalten.

Wie kann ich das mit cat , awk , cut usw. machen?

Sie können shuf . Auf einigen Systemen zumindest (scheint nicht in POSIX zu sein).

Wie jleedev sagte: sort -R könnte auch eine Option sein. Auf einigen Systemen mindestens; naja, du bekommst das Bild. Es wurde darauf hingewiesen, dass sort -R nicht wirklich mischt, sondern Elemente nach ihrem Hash-Wert sortiert.

[Anmerkung des Herausgebers: sort -R fast mischt, außer dass doppelte Zeilen / Sortierschlüssel immer nebeneinander stehen . Mit anderen Worten: Nur mit eindeutigen Eingabezeilen / -schlüsseln ist es ein echter Shuffle. Es stimmt zwar, dass die Ausgabereihenfolge durch Hash-Werte bestimmt wird, die Zufälligkeit ergibt sich jedoch aus der Wahl einer zufälligen Hash- function – siehe Handbuch .]

Perl One-Liner wäre eine einfache Version von Maxims Lösung

 perl -MList::Util=shuffle -e 'print shuffle();' < myfile 

Diese Antwort ergänzt die vielen großartigen vorhandenen Antworten auf folgende Weise:

  • Die vorhandenen Antworten sind in flexible Shell-functionen gepackt :

    • Die functionen übernehmen nicht nur die Eingabe von stdin , sondern auch Argumente von Dateinamen
    • Die functionen SIGPIPE zusätzliche Schritte, um SIGPIPE in der üblichen Weise zu handhaben (stiller Abbruch mit Exit-Code 141 ), im Gegensatz zum lautstarken Brechen. Dies ist wichtig, wenn der functionsausgang an eine Leitung geleitet wird, die früh geschlossen wird, z.
  • Ein performancesvergleich wird gemacht.


  • POSIX-konforme function basierend auf awk , sort und cut , angepasst aus der OP-eigenen Antwort :
 shuf() { awk 'BEGIN {srand(); OFMT="%.17f"} {print rand(), $0}' "$@" | sort -k1,1n | cut -d ' ' -f2-; } 
  • Perl- basierte function – angepasst an Moonyoung Kangs Antwort :
 shuf() { perl -MList::Util=shuffle -e 'print shuffle(<>);' "$@"; } 
  • Python- basierte function, angepasst an die Antwort von scai :
 shuf() { python -c ' import sys, random, fileinput; from signal import signal, SIGPIPE, SIG_DFL; signal(SIGPIPE, SIG_DFL); lines=[line for line in fileinput.input()]; random.shuffle(lines); sys.stdout.write("".join(lines)) ' "$@"; } 
  • Ruby- basierte function, abgeleitet von hoffmancs Antwort :
 shuf() { ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT"); puts ARGF.readlines.shuffle' "$@"; } 

performancesvergleich:

Hinweis: Diese Zahlen wurden auf einem iMac Ende 2012 mit 3,2 GHz Intel Core i5 und einem Fusion Drive mit OSX 10.10.3 erzielt. Die Timings unterscheiden sich je nach verwendetem Betriebssystem, verwendeter Maschinenspezifikation, verwendeter awk Implementierung (z. B. ist die auf OSX verwendete BSD- awk Version in der Regel langsamer als GNU awk und insbesondere mawk ). Dies sollte einen allgemeinen Eindruck von der relativen performance vermitteln .

Die Eingabedatei ist eine 1-Millionen-Zeilen-Datei, die mit seq -f 'line %.0f' 1000000 .
Die Zeiten sind in aufsteigender Reihenfolge aufgelistet (schnellste zuerst):

  • shuf
    • 0.090s
  • Ruby 2.0.0
    • 0.289s
  • Perl 5.18.2
    • 0.589s
  • Python
    • 1.342s mit Python 2.7.6; 2.407s (!) Mit Python 3.4.2
  • awk + sort + cut
    • 3.003s mit BSD awk ; 2.388s mit GNU awk (4.1.1); 1.811s mit mawk (1.3.4);

Zum weiteren Vergleich sind die Lösungen nicht wie oben beschrieben verpackt:

  • sort -R (keine echte Mischung, wenn doppelte Eingabezeilen vorhanden sind)
    • 10.661s – das 10.661s mehr Speicher scheint keinen Unterschied zu machen
  • Scala
    • 24.229s
  • bash Schleifen + sort
    • 32.593s

Schlussfolgerungen :

  • Verwenden Sie shuf , wenn Sie können – es ist mit Abstand das schnellste.
  • Ruby geht es gut, gefolgt von Perl .
  • Python ist merklich langsamer als Ruby und Perl, und 2.7.6 ist beim Vergleich von Python-Versionen wesentlich schneller als 3.4.1
  • Verwenden Sie die POSIX-kompatible Kombination awk + sort + cut als letztes Mittel ; mawk awk Implementierung Sie verwenden, ist wichtig ( mawk ist schneller als GNU awk , BSD awk ist am langsamsten).
  • Bleib weg von der sort -R , bash Loops und Scala.

Ich benutze ein kleines Perl-Skript, das ich “unsortieren” nenne:

 #!/usr/bin/perl use List::Util 'shuffle'; @list = ; print shuffle(@list); 

Ich habe auch eine NULL-getrennte Version, genannt “unsort0” … praktisch für die Verwendung mit find-print0 und so weiter.

PS: Auch “Shuf” gewählt, ich hatte keine Ahnung, dass es in diesen Tagen in Coreutils war … das oben genannte kann immer noch nützlich sein, wenn deine Systeme kein “Shuf” haben.

Hier ist ein erster Versuch, der für den Coder einfach ist, aber schwer für die CPU, der jeder Zeile eine Zufallszahl voranstellt, sie sortiert und dann die Zufallszahl aus jeder Zeile entfernt. In der Tat sind die Zeilen zufällig sortiert:

 cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled 

Hier ist ein awk-Skript

 awk 'BEGIN{srand() } { lines[++d]=$0 } END{ while (1){ if (e==d) {break} RANDOM = int(1 + rand() * d) if ( RANDOM in lines ){ print lines[RANDOM] delete lines[RANDOM] ++e } } }' file 

Ausgabe

 $ cat file 1 2 3 4 5 6 7 8 9 10 $ ./shell.sh 7 5 10 9 6 8 2 1 3 4 

Ein One-Liner für Python:

 python -c "import random, sys; lines = open(sys.argv[1]).readlines(); random.shuffle(lines); print ''.join(lines)," myFile 

Und um nur eine zufällige Zeile zu drucken:

 python -c "import random, sys; print random.choice(open(sys.argv[1]).readlines())," myFile 

Aber sieh diesen Beitrag für die Nachteile von random.shuffle() . Es wird mit vielen (mehr als 2080) Elementen nicht gut funktionieren.

Einfache awk-basierte function wird die Aufgabe erledigen:

 shuffle() { awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' | sort -n | cut -c8- } 

Verwendung:

 any_command | shuffle 

Dies sollte auf fast jedem UNIX funktionieren. Getestet unter Linux, Solaris und HP-UX.

Aktualisieren:

Beachten Sie, dass die Multiplikationen mit führenden Nullen ( %06d ) und rand() es auch auf Systemen richtig machen, in denen sort Zahlen nicht versteht. Es kann über lexikographische Reihenfolge sortiert werden (alias normaler Zeichenkettenvergleich).

Ruby FTW:

 ls | ruby -e 'puts STDIN.readlines.shuffle' 

Ein Liner für Python basierend auf der Antwort von scai , aber a) nimmt stdin, b) macht das Ergebnis mit Seed wiederholbar, c) wählt nur 200 aller Zeilen aus.

 $ cat file | python -c "import random, sys; random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \ > 200lines.txt 

Dies ist ein Python-Skript, das ich als rand.py in meinem Home-Ordner gespeichert habe:

 #!/bin/python import sys import random if __name__ == '__main__': with open(sys.argv[1], 'r') as f: flist = f.readlines() random.shuffle(flist) for line in flist: print line.strip() 

Unter Mac OSX sind die sort -R und shuf nicht verfügbar. Sie können diese also in Ihrem Bash_profile wie folgt umbenennen:

 alias shuf='python rand.py' 

Wir haben ein Paket, um den Job zu machen:

 sudo apt-get install randomize-lines 

Beispiel:

Erstellen Sie eine geordnete Liste von Zahlen und speichern Sie sie in 1000.txt:

 seq 1000 > 1000.txt 

um es zu mischen, einfach benutzen

 rl 1000.txt 

Wenn du wie ich hier bist, um nach einer Alternative für macOS zu suchen, dann benutze randomize-lines .

Installiere randomize-lines (Homebrew) -Paket, das einen rl Befehl hat, der eine ähnliche functionalität wie shuf .

brew install randomize-lines

 Usage: rl [OPTION]... [FILE]... Randomize the lines of a file (or stdin). -c, --count=N select N lines from the file -r, --reselect lines may be selected multiple times -o, --output=FILE send output to file -d, --delimiter=DELIM specify line delimiter (one character) -0, --null set line delimiter to null character (useful with find -print0) -n, --line-number print line number with output lines -q, --quiet, --silent do not output any errors or warnings -h, --help display this help and exit -V, --version output version information and exit 

Ein einfacher und intuitiver Weg wäre die Verwendung von shuf .

Beispiel:

Nimm words.txt als:

 the an linux ubuntu life good breeze 

Um die Linien zu mischen, tun Sie:

 $ shuf words.txt 

was würde die gemischten Linien zur Standardausgabe casting; Also, Sie müssen es an eine Ausgabedatei wie:

 $ shuf words.txt > shuffled_words.txt 

Ein solcher Shuffle-Lauf könnte ergeben:

 breeze the linux an ubuntu good life 

Wenn Sie Scala installiert haben, ist hier ein Einzeiler zum Mischen der Eingabe:

 ls -1 | scala -e 'for (l < - util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)' 

Diese Bash-function hat die minimale Abhängigkeit (nur sort und bash):

 shuf() { while read -rx;do echo $RANDOM$'\x1f'$x done | sort | while IFS=$'\x1f' read -rxy;do echo $y done } 

In Windows können Sie diese Batch-Datei versuchen, um Ihre Daten zu mischen.txt, Die Verwendung des Batch-Codes ist

 C:\> type list.txt | shuffle.bat > maclist_temp.txt 

Nach der Ausgabe dieses Befehls enthält maclist_temp.txt eine zufällige Liste von Zeilen.

Hoffe das hilft.

Noch nicht erwähnt:

  1. Das unsort util. Syntax (etwas Playlist orientiert):

     unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic] [--identity] [--filenames[=profile]] [--separator sep] [--concatenate] [--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null] [--linefeed] [file ...] 
  2. msort kann nach Zeile mischen, aber es ist normalerweise übertrieben:

     seq 10 | msort -jq -b -l -n 1 -cr 

Eine weitere awk Variante:

 #!/usr/bin/awk -f # usage: # awk -f randomize_lines.awk lines.txt # usage after "chmod +x randomize_lines.awk": # randomize_lines.awk lines.txt BEGIN { FS = "\n"; srand(); } { lines[ rand()] = $0; } END { for( k in lines ){ print lines[k]; } }