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
, etc?
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
, etc?
Antworten:
Sie können verwenden shuf
. Zumindest auf einigen Systemen (scheint nicht in POSIX zu sein).
Wie Jleedev betonte: sort -R
könnte auch eine Option sein. Zumindest auf einigen Systemen; Nun, Sie bekommen das Bild. Es wurde darauf hingewiesen, dass sort -R
Elemente nicht wirklich gemischt, sondern nach ihrem Hashwert sortiert werden.
[Anmerkung des Herausgebers: sort -R
fast gemischt, außer dass doppelte Zeilen / Sortierschlüssel immer nebeneinander landen . Mit anderen Worten: Nur mit eindeutigen Eingabezeilen / Tasten ist es ein echtes Shuffle. Zwar wird die Ausgabereihenfolge durch Hash-Werte bestimmt , die Zufälligkeit ergibt sich jedoch aus der Auswahl einer zufälligen Hash- Funktion - siehe Handbuch .]
shuf
und sort -R
unterscheiden sich geringfügig, da sort -R
die Elemente zufällig nach dem Hash von ihnen geordnet werden sort -R
, shuf
dh die wiederholten Elemente zusammengesetzt werden, während alle Elemente zufällig gemischt werden.
brew install coreutils
dann gshuf ...
(:
sort -R
und shuf
sollte als völlig anders angesehen werden. sort -R
ist deterministisch. Wenn Sie es zweimal zu unterschiedlichen Zeiten am selben Eingang aufrufen, erhalten Sie dieselbe Antwort. shuf
Auf der anderen Seite wird eine zufällige Ausgabe erzeugt, sodass höchstwahrscheinlich unterschiedliche Ausgaben für dieselbe Eingabe ausgegeben werden.
Perl One-Liner wäre eine einfache Version von Maxim's Lösung
perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
\n
. ja, das \n
muss vorhanden sein - und es in der Regel ist - sonst werden Sie bekommen , was Sie beschreiben.
<STDIN>
mit <>
, so dass die Lösung mit dem Input von arbeitet Dateien zu.
Diese Antwort ergänzt die vielen großen vorhandenen Antworten auf folgende Weise:
Die vorhandenen Antworten sind in flexible Shell-Funktionen gepackt :
stdin
Eingang, sondern alternativ auch Dateinamen ArgumenteSIGPIPE
gewohnt zu handhaben (leiser Abschluss mit Exit-Code 141
), anstatt laut zu brechen. Dies ist wichtig, wenn Sie den Funktionsausgang an ein Rohr weiterleiten, das vorzeitig geschlossen wird, z head
.Ein Leistungsvergleich wird durchgeführt.
awk
, sort
undcut
, angepasst von dem eigenen Antwort des OP :shuf() { awk 'BEGIN {srand(); OFMT="%.17f"} {print rand(), $0}' "$@" |
sort -k1,1n | cut -d ' ' -f2-; }
shuf() { perl -MList::Util=shuffle -e 'print shuffle(<>);' "$@"; }
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))
' "$@"; }
Im unteren Abschnitt finden Sie eine Windows- Version dieser Funktion.
shuf() { ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT");
puts ARGF.readlines.shuffle' "$@"; }
Leistungsvergleich:
Hinweis: Diese Zahlen wurden auf einem iMac Ende 2012 mit 3,2 GHz Intel Core i5 und einem Fusion Drive unter OSX 10.10.3 ermittelt. Während die Zeitabläufe je nach verwendetem Betriebssystem, Maschinenspezifikationen und verwendeter awk
Implementierung variieren (z. B. ist die awk
unter OSX verwendete BSD- Version normalerweise langsamer als GNU awk
und insbesondere mawk
), sollte dies einen allgemeinen Eindruck von der relativen Leistung vermitteln .
Die Eingabedatei ist eine 1-Millionen-Zeilen-Datei, die mit erstellt wurde seq -f 'line %.0f' 1000000
.
Die Zeiten sind in aufsteigender Reihenfolge aufgeführt (am schnellsten zuerst):
shuf
0.090s
0.289s
0.589s
1.342s
mit Python 2.7.6; 2.407s
(!) mit Python 3.4.2awk
+ 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
(kein echtes Shuffle, wenn doppelte Eingabezeilen vorhanden sind)
10.661s
- Das Zuweisen von mehr Speicher scheint keinen Unterschied zu machen24.229s
bash
Schleifen + sort
32.593s
Schlussfolgerungen :
shuf
, wenn Sie können - es ist bei weitem das schnellste.awk
+ sort
+ cut
Kombination als letzten Ausweg . Welche awk
Implementierung Sie verwenden, ist wichtig ( mawk
ist schneller als GNU awk
, BSD awk
ist am langsamsten).sort -R
, bash
Loops und Scala.Windows- Versionen der Python- Lösung (der Python-Code ist identisch, mit Ausnahme von Anführungszeichen und dem Entfernen der signalbezogenen Anweisungen, die unter Windows nicht unterstützt werden):
$OutputEncoding
wenn Sie Nicht-ASCII-Zeichen über die Pipeline senden möchten):# Call as `shuf someFile.txt` or `Get-Content someFile.txt | shuf`
function shuf {
$Input | python -c @'
import sys, random, fileinput;
lines=[line for line in fileinput.input()];
random.shuffle(lines); sys.stdout.write(''.join(lines))
'@ $args
}
Beachten Sie, dass PowerShell nativ über sein Get-Random
Cmdlet mischen kann (obwohl die Leistung ein Problem sein kann). z.B:
Get-Content someFile.txt | Get-Random -Count ([int]::MaxValue)
cmd.exe
(eine Batch-Datei):In Datei speichern shuf.cmd
, zum Beispiel:
@echo off
python -c "import sys, random, fileinput; lines=[line for line in fileinput.input()]; random.shuffle(lines); sys.stdout.write(''.join(lines))" %*
python -c "import sys, random; lines = [x for x in sys.stdin.read().splitlines()] ; random.shuffle(lines); print(\"\n\".join([line for line in lines]));"
from signal import signal, SIGPIPE, SIG_DFL; signal(SIGPIPE, SIG_DFL);
von der ursprünglichen Lösung ist ausreichend, und behält die Flexibilität auch Dateinamen in der Lage zu übergeben Argumente - keine Notwendigkeit zu ändern etwas anderes (außer zitieren) - Bitte beachten Sie den neuen Abschnitt I bei der hinzugefügt habe Unterseite.
Ich benutze ein winziges Perl-Skript, das ich "unsortieren" nenne:
#!/usr/bin/perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);
Ich habe auch eine durch NULL getrennte Version namens "unsort0" ... praktisch für die Verwendung mit find -print0 und so weiter.
PS: Ich habe auch 'shuf' gewählt und hatte keine Ahnung, dass es heutzutage in coreutils gibt ... das oben Genannte kann immer noch nützlich sein, wenn Ihre Systeme kein 'shuf' haben.
<STDIN>
mit <>
, um die Lösung der Arbeit mit dem Input von zu machen Dateien zu.
Hier ist ein erster Versuch, der für den Codierer einfach, aber für die CPU schwierig ist. Er stellt jeder Zeile eine Zufallszahl voran, sortiert sie und entfernt dann die Zufallszahl von jeder Zeile. Tatsächlich werden die Zeilen zufällig sortiert:
cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled
head myfile | awk ...
. Dann ändere ich es einfach in Katze; deshalb wurde es dort gelassen.
-k1 -n
Sortierung erforderlich , da die Ausgabe von awk rand()
eine Dezimalstelle zwischen 0 und 1 ist und alles, was zählt, ist, dass sie irgendwie neu angeordnet wird. -k1
könnte helfen, es zu beschleunigen, indem der Rest der Zeile ignoriert wird, obwohl die Ausgabe von rand () eindeutig genug sein sollte, um den Vergleich kurzzuschließen.
cat filename |
(oder < filename |
) beizubehalten, als sich daran zu erinnern, wie jedes einzelne Programm Dateieingaben übernimmt (oder nicht).
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
awk
mit sort
und kombiniert wird cut
. Für nicht mehr als mehrere Tausend Zeilen macht es keinen großen Unterschied, aber bei höheren Zeilenzahlen ist es wichtig (der Schwellenwert hängt von der verwendeten awk
Implementierung ab). Eine leichte Vereinfachung wäre das Ersetzen von Zeilen while (1){
und if (e==d) {break}
durch while (e<d)
.
Ein Einzeiler für Python:
python -c "import random, sys; lines = open(sys.argv[1]).readlines(); random.shuffle(lines); print ''.join(lines)," myFile
Und zum Drucken nur einer einzigen zufälligen Zeile:
python -c "import random, sys; print random.choice(open(sys.argv[1]).readlines())," myFile
Aber siehe diesen Beitrag für die Nachteile von Python random.shuffle()
. Es funktioniert nicht gut mit vielen (mehr als 2080) Elementen.
/dev/urandom
dies mit Entropie aus dem System neu ausgesät wird. So verwenden Sie es aus Python : random.SystemRandom().shuffle(L)
.
.readLines()
die Zeilen mit einem nachgestellten Zeilenumbruch zurückgegeben werden.
Einfache awk-basierte Funktion erledigt den Job:
shuffle() {
awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' | sort -n | cut -c8-
}
Verwendung:
any_command | shuffle
Dies sollte unter fast jedem UNIX funktionieren. Getestet unter Linux, Solaris und HP-UX.
Aktualisieren:
Beachten Sie, dass führende Nullen ( %06d
) und rand()
Multiplikation dazu führen, dass es auch auf Systemen, auf denen sort
Zahlen nicht verstanden werden, ordnungsgemäß funktioniert . Es kann nach lexikografischer Reihenfolge sortiert werden (auch bekannt als normaler Zeichenfolgenvergleich).
"$@"
, funktioniert es auch mit Dateien als Eingabe. Es gibt keinen Grund zur Multiplikation rand()
, da sort -n
Dezimalbrüche sortiert werden können. Es ist jedoch eine gute Idee, Kontrolle awk
‚s Ausgabeformat, denn mit dem Standardformat, %.6g
, rand()
ausgeben wird die gelegentliche Zahl in exponentieller Notation. Während das Mischen von bis zu 1 Million Zeilen in der Praxis wohl ausreicht, ist es einfach, mehr Zeilen zu unterstützen, ohne einen großen Leistungsverlust zu zahlen. zB %.17f
.
sort
sollte es im Allgemeinen möglich sein, Dezimalbrüche zu verarbeiten (auch mit Tausenden von Trennzeichen, wie ich gerade bemerkt habe).
Ruby FTW:
ls | ruby -e 'puts STDIN.readlines.shuffle'
puts ARGF.readlines.shuffle
, können Sie dafür sorgen, dass es sowohl mit stdin-Eingabe- als auch mit Dateinamenargumenten funktioniert.
ruby -e 'puts $<.sort_by{rand}'
- ARGF ist bereits eine Aufzählung, sodass wir die Zeilen mischen können, indem wir sie nach zufälligen Werten sortieren.
Ein Liner für Python basiert auf Scais Antwort , 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
Eine einfache und intuitive Möglichkeit wäre die Verwendung shuf
.
Beispiel:
Angenommen, words.txt
als:
the
an
linux
ubuntu
life
good
breeze
Um die Zeilen zu mischen, gehen Sie wie folgt vor:
$ shuf words.txt
das würde die gemischten Zeilen auf Standardausgabe werfen ; Also, haben Sie zu Rohr es zu einer Ausgabedatei wie:
$ shuf words.txt > shuffled_words.txt
Ein solcher Shuffle-Lauf könnte ergeben:
breeze
the
linux
an
ubuntu
good
life
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 sort -R
und shuf
nicht verfügbar, können Sie dies in Ihrem bash_profile als Alias verwenden:
alias shuf='python rand.py'
Wenn Sie wie ich hierher gekommen sind, um nach einer Alternative zu shuf
macOS zu suchen, dann verwenden Sie randomize-lines
.
Installieren Sie das randomize-lines
(Homebrew-) Paket, das einen rl
Befehl hat, der ähnliche Funktionen hat 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
brew install coreutils
wird die shuf
Binärdatei als bereitgestellt gshuf
.
Wenn Sie Scala installiert haben, ist hier ein Einzeiler, um die Eingabe zu mischen:
ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'
Diese Bash-Funktion hat die minimale Abhängigkeit (nur Sortieren und Bash):
shuf() {
while read -r x;do
echo $RANDOM$'\x1f'$x
done | sort |
while IFS=$'\x1f' read -r x y;do
echo $y
done
}
awk
unterstützten Lösung entspricht, aber die Leistung wird bei größeren Eingaben ein Problem sein. $RANDOM
Wenn Sie einen einzelnen Wert verwenden, werden nur bis zu 32.768 Eingabezeilen korrekt gemischt. Sie könnten diesen Bereich zwar erweitern, aber es lohnt sich wahrscheinlich nicht: Auf meinem Computer dauert das Ausführen Ihres Skripts auf 32.768 kurzen Eingabezeilen etwa 1 Sekunde, was etwa 150-mal so lange shuf
dauert wie das Ausführen , und etwa 10-15 Mal solange die vom OP awk
unterstützte Lösung dauert. Wenn Sie sich darauf verlassen können sort
, anwesend zu sein, awk
sollten Sie auch dabei sein.
In Windows Sie können diese Batch-Datei ausprobieren , um Ihre data.txt zu mischen. 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:
Die 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 ...]
msort
kann nach Zeilen mischen, ist aber normalerweise übertrieben:
seq 10 | msort -jq -b -l -n 1 -c r