381 Stimmen

Zufällige Zeilen aus einer Datei auswählen

In einem Bash-Skript möchte ich N zufällige Zeilen aus einer Eingabedatei heraussuchen und in eine andere Datei ausgeben.

Wie lässt sich das bewerkstelligen?

849voto

dogbane Punkte 253146

使用する shuf mit dem -n wie unten gezeigt, um Folgendes zu erhalten N zufällige Linien:

shuf -n N input > output

201voto

user881480 Punkte 4645

Die Datei zufällig sortieren und zuerst auswählen 100 Zeilen:

lines=100
input_file=/usr/share/dict/words

# This is the basic selection method
<$input_file sort -R | head -n $lines

# If the file has duplicates that must never cause duplicate results
<$input_file sort | uniq        | sort -R | head -n $lines

# If the file has blank lines that must be filtered, use sed
<$input_file sed $'/^[ \t]*$/d' | sort -R | head -n $lines

Natürlich <$input_file kann durch eine beliebige Standardeingabe in der Pipeline ersetzt werden. Diese ( sort -R et $'...\t...' zu erhalten sed um Tabulatorzeichen anzupassen) funktioniert unter GNU/Linux und BSD/macOS.

31voto

Laut einem Kommentar auf die Shuf-Antwort hat er 78 000 000 000 Zeilen in weniger als einer Minute gemischt.

Herausforderung angenommen...

EDIT: Ich habe meinen eigenen Rekord geschlagen

powershuf schaffte es in 0,047 Sekunden

$ time ./powershuf.py -n 10 --file lines_78000000000.txt > /dev/null 
./powershuf.py -n 10 --file lines_78000000000.txt > /dev/null  0.02s user 0.01s system 80% cpu 0.047 total

Der Grund, warum es so schnell geht, ist, dass ich nicht die ganze Datei lese, sondern nur den Dateizeiger 10 Mal bewege und die Zeile nach dem Zeiger ausdrucke.

Gitlab Repo

Alter Versuch

Zuerst brauchte ich eine Datei mit 78.000.000.000 Zeilen:

seq 1 78 | xargs -n 1 -P 16 -I% seq 1 1000 | xargs -n 1 -P 16 -I% echo "" > lines_78000.txt
seq 1 1000 | xargs -n 1 -P 16 -I% cat lines_78000.txt > lines_78000000.txt
seq 1 1000 | xargs -n 1 -P 16 -I% cat lines_78000000.txt > lines_78000000000.txt

Dadurch erhalte ich eine Datei mit 78 Mrd. Zeilenumbrüche ;-)

Nun zum Shuffle-Teil:

$ time shuf -n 10 lines_78000000000.txt

shuf -n 10 lines_78000000000.txt  2171.20s user 22.17s system 99% cpu 36:35.80 total

Der Engpass war die CPU und die Nichtverwendung mehrerer Threads, es wurde 1 Kern zu 100% ausgelastet, die anderen 15 wurden nicht verwendet.

Python ist das, was ich regelmäßig verwende, also werde ich es auch verwenden, um das Ganze schneller zu machen:

#!/bin/python3
import random
f = open("lines_78000000000.txt", "rt")
count = 0
while 1:
  buffer = f.read(65536)
  if not buffer: break
  count += buffer.count('\n')

for i in range(10):
  f.readline(random.randint(1, count))

Damit habe ich nur knapp eine Minute gebraucht:

$ time ./shuf.py         

./shuf.py  42.57s user 16.19s system 98% cpu 59.752 total

Ich habe dies auf einem Lenovo X1 extreme 2nd gen mit dem i9 und Samsung NVMe, die mir viel Lese-und Schreibgeschwindigkeit gibt.

Ich weiß, dass es schneller gehen kann, aber ich lasse noch etwas Spielraum, um es mit anderen zu versuchen.

Zeilenzähler Quelle: Luther Blissett

11voto

Merlin Punkte 1494

Meine bevorzugte Option ist sehr schnell, ich habe eine Tabulator-getrennte Datendatei mit 13 Spalten, 23,1 Millionen Zeilen und 2,0 GB unkomprimiert untersucht.

# randomly sample select 5% of lines in file
# including header row, exclude blank lines, new seed

time \
awk 'BEGIN  {srand()} 
     !/^$/  { if (rand() <= .05 || FNR==1) print > "data-sample.txt"}' data.txt

# awk  tsv004  3.76s user 1.46s system 91% cpu 5.716 total

1voto

Andelf Punkte 530
seq 1 100 | python3 -c 'print(__import__("random").choice(__import__("sys").stdin.readlines()))'

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X