Was ist eine einfache Möglichkeit, zufällige Zeilen aus einer Datei in der Unix-Befehlszeile zu lesen?
Was ist eine einfache Möglichkeit, zufällige Zeilen aus einer Datei in der Unix-Befehlszeile zu lesen?
Antworten:
Sie können verwenden shuf
:
shuf -n 1 $FILE
Es gibt auch ein Dienstprogramm namens rl
. In Debian ist es im randomize-lines
Paket enthalten, das genau das tut, was Sie wollen, obwohl es nicht in allen Distributionen verfügbar ist. Auf seiner Homepage empfiehlt es tatsächlich die Verwendung von shuf
stattdessen (was meiner Meinung nach nicht existierte, als es erstellt wurde). shuf
ist Teil der GNU Coreutils, rl
nicht.
rl -c 1 $FILE
shuf
Tipp, es ist in Fedora integriert.
sort -R
wird auf jeden Fall viel warten lassen, wenn es sich um sehr große Dateien handelt - 80kk Zeilen -, während es shuf -n
ziemlich augenblicklich funktioniert .
coreutils
von Homebrew installieren . Könnte gshuf
statt genannt werden shuf
.
randomize-lines
unter OS X vonbrew install randomize-lines; rl -c 1 $FILE
shuf
Teil von GNU Coreutils ist und daher nicht unbedingt (standardmäßig) auf * BSD-Systemen (oder Mac?) Verfügbar ist. Der Perl-Einzeiler von @ Tracker1 unten ist portabler (und nach meinen Tests etwas schneller).
Eine andere Alternative:
head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1
(${RANDOM} << 15) + ${RANDOM}
. Dies reduziert die Verzerrung erheblich und ermöglicht es, für Dateien mit bis zu 1 Milliarde Zeilen zu arbeiten.
+
und |
sind gleich, da ${RANDOM}
per Definition 0..32767 ist.
sort --random-sort $FILE | head -n 1
(Ich mag den Shuf-Ansatz oben noch besser - ich wusste nicht einmal, dass es ihn gibt und ich hätte dieses Tool alleine nie gefunden)
sort
, die auf keinem meiner Systeme funktioniert hat (CentOS 5.5, Mac OS 10.7.2). Auch nutzloser Gebrauch von Katze, könnte aufsort --random-sort < $FILE | head -n 1
sort -R <<< $'1\n1\n2' | head -1
ist ebenso wahrscheinlich, 1 und 2 zurückzugeben, da sort -R
doppelte Zeilen zusammen sortiert werden. Gleiches gilt sort -Ru
, weil dadurch doppelte Zeilen entfernt werden.
sort
vor dem Weiterleiten gemischt werden muss head
. shuf
wählt stattdessen zufällige Zeilen aus der Datei aus und ist für mich viel schneller.
sort --random-sort $FILE | head
wäre am besten, da es ihm ermöglicht, direkt auf die Datei zuzugreifen, was möglicherweise eine effiziente parallele Sortierung ermöglicht
--random-sort
und -R
sind spezifisch für die GNU-Sortierung (daher funktionieren sie nicht mit BSD oder Mac OS sort
). GNU sort hat diese Flags im Jahr 2005 gelernt, sodass Sie GNU Coreutils 6.0 oder neuer (z. B. CentOS 6) benötigen.
Das ist einfach.
cat file.txt | shuf -n 1
Zugegeben, dies ist nur ein bisschen langsamer als die "shuf -n 1 file.txt" für sich.
-n 1
1 Zeile angegeben ist und Sie diese in mehr als 1 ändern können. shuf
Kann auch für andere Zwecke verwendet werden. Ich habe gerade gepfeift ps aux
und grep
damit zufällig Prozesse beendet, die teilweise mit einem Namen übereinstimmen.
perlfaq5: Wie wähle ich eine zufällige Zeile aus einer Datei aus? Hier ist ein Reservoir-Sampling-Algorithmus aus dem Camel Book:
perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' file
Dies hat einen erheblichen räumlichen Vorteil gegenüber dem Einlesen der gesamten Datei. Einen Beweis für diese Methode finden Sie in The Art of Computer Programming, Band 2, Abschnitt 3.4.2, von Donald E. Knuth.
shuf
. Der Perl-Code ist etwas schneller (8% schneller nach Benutzerzeit, 24% schneller nach Systemzeit), obwohl ich anekdotisch festgestellt habe, dass der Perl-Code weniger zufällig "scheint" (ich habe eine Jukebox damit geschrieben).
shuf
Speichert die gesamte Eingabedatei im Speicher , was eine schreckliche Idee ist, während dieser Code nur eine Zeile speichert, sodass die Grenze dieses Codes eine Zeilenanzahl von INT_MAX ist (2 ^ 31 oder 2 ^ 63, abhängig von Ihrer arch), unter der Annahme, dass eine der ausgewählten potenziellen Linien in den Speicher passt.
Verwenden eines Bash-Skripts:
#!/bin/bash
# replace with file to read
FILE=tmp.txt
# count number of lines
NUM=$(wc - l < ${FILE})
# generate random number in range 0-NUM
let X=${RANDOM} % ${NUM} + 1
# extract X-th line
sed -n ${X}p ${FILE}
Einzelne Bash-Linie:
sed -n $((1+$RANDOM%`wc -l test.txt | cut -f 1 -d ' '`))p test.txt
Leichtes Problem: doppelter Dateiname.
wc -l < test.txt
vermeidet das Weiterleiten cut
.
Hier ist ein einfaches Python-Skript, das die Arbeit erledigt:
import random, sys
lines = open(sys.argv[1]).readlines()
print(lines[random.randrange(len(lines))])
Verwendungszweck:
python randline.py file_to_get_random_line_from
import random, sys lines = open(sys.argv[1]).readlines()
Folgendes getan: für i im Bereich (len (Linien)): rand = random.randint (0, len (Linien) -1) print lines.pop (rand),
len(lines)
kann daher zu IndexError führen. Sie könnten verwenden print(random.choice(list(open(sys.argv[1]))))
. Es gibt auch einen speichereffizienten Reservoir-Abtastalgorithmus .
Ein anderer Weg mit ' awk '
awk NR==$((${RANDOM} % `wc -l < file.name` + 1)) file.name
$RANDOM
ist ein Bashismus ). Hier ist eine reine awk (mawk) -Methode, die dieselbe Logik wie der oben zitierte Perlfaq5-Code von @ Tracker1 verwendet: awk 'rand() * NR < 1 { line = $0 } END { print line }' file.name
(Wow, es ist sogar noch kürzer als der Perl-Code!)
wc
) lesen , um eine Zeilenanzahl zu erhalten, und muss dann (einen Teil) der Datei erneut lesen ( awk
), um den Inhalt der angegebenen zufälligen Zeilennummer zu erhalten. E / A sind weitaus teurer als das Erhalten einer Zufallszahl. Mein Code liest die Datei nur einmal. Das Problem bei awk's rand()
ist, dass es auf Sekunden basiert, sodass Sie Duplikate erhalten, wenn Sie es nacheinander zu schnell ausführen.
Eine Lösung, die auch unter MacOSX funktioniert und auch unter Linux (?) Funktionieren sollte:
N=5
awk 'NR==FNR {lineN[$1]; next}(FNR in lineN)' <(jot -r $N 1 $(wc -l < $file)) $file
Wo:
N
ist die Anzahl der gewünschten zufälligen Zeilen
NR==FNR {lineN[$1]; next}(FNR in lineN) file1 file2
-> Speichern Sie die eingegebenen Zeilennummern file1
und drucken Sie die entsprechende Zeile einfile2
jot -r $N 1 $(wc -l < $file)
-> N
Zahlen zufällig ( -r
) im Bereich (1, number_of_line_in_file)
mit zeichnen jot
. Durch die Prozessersetzung <()
sieht es wie eine Datei für den Interpreter aus, also file1
im vorherigen Beispiel.#!/bin/bash
IFS=$'\n' wordsArray=($(<$1))
numWords=${#wordsArray[@]}
sizeOfNumWords=${#numWords}
while [ True ]
do
for ((i=0; i<$sizeOfNumWords; i++))
do
let ranNumArray[$i]=$(( ( $RANDOM % 10 ) + 1 ))-1
ranNumStr="$ranNumStr${ranNumArray[$i]}"
done
if [ $ranNumStr -le $numWords ]
then
break
fi
ranNumStr=""
done
noLeadZeroStr=$((10#$ranNumStr))
echo ${wordsArray[$noLeadZeroStr]}
Folgendes entdecke ich, da mein Mac OS nicht alle einfachen Antworten verwendet. Ich habe den Befehl jot verwendet, um eine Zahl zu generieren, da die variablen Lösungen von $ RANDOM in meinem Test nicht sehr zufällig zu sein scheinen. Beim Testen meiner Lösung gab es große Unterschiede bei den in der Ausgabe angegebenen Lösungen.
RANDOM1=`jot -r 1 1 235886`
#range of jot ( 1 235886 ) found from earlier wc -w /usr/share/dict/web2
echo $RANDOM1
head -n $RANDOM1 /usr/share/dict/web2 | tail -n 1
Das Echo der Variablen dient dazu, eine visuelle Darstellung der generierten Zufallszahl zu erhalten.
Wenn Sie nur Vanille sed und awk verwenden und $ RANDOM nicht verwenden, ist ein einfacher, platzsparender und relativ schneller "Einzeiler" zum Auswählen einer einzelnen Zeile pseudozufällig aus einer Datei mit dem Namen FILENAME wie folgt:
sed -n $(awk 'END {srand(); r=rand()*NR; if (r<NR) {sub(/\..*/,"",r); r++;}; print r}' FILENAME)p FILENAME
(Dies funktioniert auch, wenn FILENAME leer ist. In diesem Fall wird keine Zeile ausgegeben.)
Ein möglicher Vorteil dieses Ansatzes besteht darin, dass rand () nur einmal aufgerufen wird.
Wie von @AdamKatz in den Kommentaren hervorgehoben, besteht eine andere Möglichkeit darin, rand () für jede Zeile aufzurufen:
awk 'rand() * NR < 1 { line = $0 } END { print line }' FILENAME
(Ein einfacher Korrektheitsnachweis kann auf der Grundlage der Induktion erbracht werden.)
rand()
"In den meisten awk-Implementierungen, einschließlich gawk, generiert rand () jedes Mal, wenn Sie awk ausführen, Zahlen aus derselben Startnummer oder demselben Startwert."
- https://www.gnu.org/software/gawk/manual/html_node/Numeric-Functions.html