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-linesPaket 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 shufstattdessen (was meiner Meinung nach nicht existierte, als es erstellt wurde). shufist Teil der GNU Coreutils, rlnicht.
rl -c 1 $FILE
shufTipp, es ist in Fedora integriert.
sort -Rwird auf jeden Fall viel warten lassen, wenn es sich um sehr große Dateien handelt - 80kk Zeilen -, während es shuf -nziemlich augenblicklich funktioniert .
coreutilsvon Homebrew installieren . Könnte gshufstatt genannt werden shuf.
randomize-linesunter OS X vonbrew install randomize-lines; rl -c 1 $FILE
shufTeil 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 -1ist ebenso wahrscheinlich, 1 und 2 zurückzugeben, da sort -Rdoppelte Zeilen zusammen sortiert werden. Gleiches gilt sort -Ru, weil dadurch doppelte Zeilen entfernt werden.
sortvor dem Weiterleiten gemischt werden muss head. shufwählt stattdessen zufällige Zeilen aus der Datei aus und ist für mich viel schneller.
sort --random-sort $FILE | headwäre am besten, da es ihm ermöglicht, direkt auf die Datei zuzugreifen, was möglicherweise eine effiziente parallele Sortierung ermöglicht
--random-sortund -Rsind 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 11 Zeile angegeben ist und Sie diese in mehr als 1 ändern können. shufKann auch für andere Zwecke verwendet werden. Ich habe gerade gepfeift ps auxund grepdamit 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).
shufSpeichert 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.txtvermeidet 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
$RANDOMist 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 file1und drucken Sie die entsprechende Zeile einfile2
jot -r $N 1 $(wc -l < $file)-> NZahlen 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 file1im 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