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?
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?
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.
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
$ 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.
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
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
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.