Ich möchte die Zeilen einer Textdatei nach dem Zufallsprinzip mischen und eine neue Datei erstellen. Die Datei kann mehrere tausend Zeilen enthalten.
Wie mache ich das mit cat
, awk
, cut
usw.?
Sie können shuf
verwenden. Zumindest auf einigen Systemen (scheint nicht in POSIX zu sein).
Wie Jleedev darauf hinwies: sort -R
könnte auch eine Option sein. Zumindest auf einigen Systemen; Nun, du bekommst das Bild. Es wurde darauf hingewiesen , dass sort -R
nicht wirklich mischt, sondern Elemente nach ihrem Hashwert sortiert.
[Anmerkung des Editors: sort -R
fast mischt, außer dass duplicate Zeilen/Sortierschlüssel immer nebeneinander stehen. Mit anderen Worten: Nur mit unique Eingabezeilen/Tasten ist es ein wahrer Zufall. Es ist zwar wahr, dass die Ausgabereihenfolge durch Hashwerte bestimmt wird, die Zufälligkeit ergibt sich jedoch aus der Wahl eines zufälligen Hashes Funktion - siehe manual .]
Perl-Einzeiler wäre eine einfache Version der Lösung von Maxim
Perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
Diese Antwort ergänzt die vielen großartigen vorhandenen Antworten auf folgende Weise:
Die vorhandenen Antworten sind in flexible Shell-Funktionen gepackt:
stdin
Eingaben, sondern alternativ auch DateinameArgumente SIGPIPE
auf die übliche Weise zu behandeln (stille Beendigung mit Exit-Code 141
), im Gegensatz zu lauten zu brechen. Dies ist wichtig, wenn die Funktionsausgabe an eine Pipe weitergeleitet wird, die vorzeitig geschlossen wird, z. B. wenn an head
weitergeleitet wird.Ein Leistungsvergleich wird durchgeführt.
awk
, sort
und cut
, angepasst von OPs eigene Antwort :shuf() { awk 'BEGIN {srand(); OFMT="%.17f"} {print Rand(), $0}' "[email protected]" |
sort -k1,1n | cut -d ' ' -f2-; }
shuf() { Perl -MList::Util=shuffle -e 'print shuffle(<>);' "[email protected]"; }
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))
' "[email protected]"; }
shuf() { Ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT");
puts ARGF.readlines.shuffle' "[email protected]"; }
Leistungsvergleich:
Hinweis: Diese Zahlen wurden auf einem iMac von Ende 2012 mit 3,2 GHz Intel Core i5 und einem Fusion-Laufwerk unter OSX 10.10.3 ermittelt. Während das Timing mit dem verwendeten Betriebssystem, den Maschinenspezifikationen und der verwendeten awk
-Implementierung variiert (z. B. der verwendeten BSD awk
-Version) unter OSX ist es normalerweise langsamer als GNU awk
und insbesondere mawk
), dies sollte einen allgemeinen Sinn für relativeperformance .
Eingabedatei ist eine 1-Millionen-Zeilen-Datei , die mit seq -f 'line %.0f' 1000000
.
Die Zeiten sind in aufsteigender Reihenfolge aufgeführt (schnellste 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, die nicht als Funktionen verpackt sind, wie folgt:
sort -R
(kein wahres Mischen, wenn es doppelte Eingabezeilen gibt) 10.661s
- Die Zuweisung 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
als letzten Ausweg ; Welche awk
Implementierung Sie verwenden, ist von Bedeutung (mawk
ist schneller als GNU awk
, BSD awk
ist am langsamsten).sort -R
, bash
Schleifen und Scala.Ich benutze ein kleines Perl-Skript, das ich "unsortieren" nenne:
#!/usr/bin/Perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);
Ich habe auch eine NULL-begrenzte Version namens "unsort0" ... praktisch für die Verwendung mit find -print0 und so weiter.
PS: Ich habe auch 'shuf' gewählt, ich hatte keine Ahnung, dass es heutzutage in coreutils war ... das oben genannte kann immer noch nützlich sein, wenn Ihr System nicht 'shuf' hat.
Hier ist ein erster Versuch, der für den Codierer einfach ist, aber hart für die CPU, die jeder Zeile eine Zufallszahl voranstellt, sie sortiert und dann die Zufallszahl aus jeder Zeile entfernt. Tatsächlich werden 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-script
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 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 einzige zufällige Zeile zu drucken:
python -c "import random, sys; print random.choice(open(sys.argv[1]).readlines())," myFile
Aber siehe diesen Beitrag für die Nachteile von Pythons random.shuffle()
. Mit vielen (mehr als 2080) Elementen funktioniert es nicht gut.
Eine einfache, auf AWS basierende Funktion erledigt die Arbeit:
shuffle() {
awk 'BEGIN{srand();} {printf "%06d %s\n", Rand()*1000000, $0;}' | sort -n | cut -c8-
}
verwendungszweck:
any_command | shuffle
Dies sollte auf fast jedem UNIX funktionieren. Getestet unter Linux, Solaris und HP-UX.
Update:
Beachten Sie, dass die Multiplikation führender Nullen (%06d
) und Rand()
es auch auf Systemen ermöglicht, auf denen sort
Zahlen nicht versteht. Es kann über die lexikografische Reihenfolge sortiert werden (auch normaler Vergleich von Strings).
Ruby FTW:
ls | Ruby -e 'puts STDIN.readlines.shuffle'
Ein Liner für Python basierend auf scais Antwort , aber a) nimmt stdin, b) macht das Ergebnis mit Seed wiederholbar, c) wählt nur 200 von allen Zeilen aus.
$ cat file | python -c "import random, sys;
random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \
> 200lines.txt
Ein einfacher und intuitiver Weg wäre die Verwendung von shuf
.
Beispiel:
Angenommen, words.txt
als:
the
an
linux
ubuntu
life
good
breeze
Mischen Sie die Zeilen wie folgt:
$ shuf words.txt
das würde die gemischten Zeilen nach Standardausgabe werfen; Also müssen Sie Pipe zu einer Ausgabedatei wie folgt:
$ shuf words.txt > shuffled_words.txt
Ein solcher Shuffle Run könnte ergeben:
breeze
the
linux
an
ubuntu
good
life
Wir haben ein Paket, um den Job zu erledigen:
Sudo apt-get install randomize-lines
Beispiel:
Erstellen Sie eine geordnete Liste von Nummern und speichern Sie sie in 1000.txt:
seq 1000 > 1000.txt
um es zu mischen, verwenden Sie einfach
rl 1000.txt
Wenn Sie wie ich hierher gekommen sind, um nach einer Alternative zu shuf
für macOS zu suchen, verwenden Sie randomize-lines
.
Installieren Sie das Paket randomize-lines
(homebrew), das einen rl
-Befehl mit ähnlicher Funktionalität wie shuf
enthält.
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
Dies ist ein Python-Skript, das ich als Rand.py in meinem Home-Ordner gespeichert habe:
#!/bin/python
import sys
import random
if __== '__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 sort -R
und shuf
nicht verfügbar. Sie können dies in Ihrem bash_profile als Alias angeben:
alias shuf='python Rand.py'
Wenn Sie Scala installiert haben, können Sie die Eingabe mit einem Einzeiler 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 sort 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
}
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 mischen, aber normalerweise ist es übertrieben:
seq 10 | msort -jq -b -l -n 1 -c r
Eine andere 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];
}
}
In Windows können Sie versuchen, diese Batchdatei , um Ihre Daten zu mischen.txt. Die Verwendung des Batchcodes ist
C:\> type list.txt | shuffle.bat > maclist_temp.txt
Nachdem Sie diesen Befehl ausgegeben haben, enthält maclist_temp.txt eine randomisierte Liste von Zeilen.
Hoffe das hilft.