Apa cara mudah untuk membaca baris acak dari file di baris perintah Unix?
linux
unix
random
command-line
codeforester
sumber
sumber
Jawaban:
Anda bisa menggunakan
shuf
:Ada juga utilitas yang disebut
rl
. Di Debian ada dalamrandomize-lines
paket yang melakukan persis apa yang Anda inginkan, meskipun tidak tersedia di semua distro. Di halaman beranda sebenarnya merekomendasikan penggunaanshuf
sebagai gantinya (yang tidak ada saat itu dibuat, saya percaya).shuf
adalah bagian dari GNU coreutils,rl
bukan.sumber
shuf
tipnya, ini built-in di Fedora.sort -R
pasti akan membuat orang menunggu banyak jika berurusan dengan file yang sangat besar - 80kb baris -, sedangkan,shuf -n
bertindak cukup instan.coreutils
dari Homebrew. Mungkin bisa disebutgshuf
bukanshuf
.randomize-lines
OS X olehbrew install randomize-lines; rl -c 1 $FILE
shuf
ini adalah bagian dari GNU Coreutils dan karena itu tidak akan selalu tersedia (secara default) pada sistem * BSD (atau Mac?). Perl satu-liner @ tracker1 di bawah ini lebih portabel (dan menurut tes saya, sedikit lebih cepat)Alternatif lain:
sumber
(${RANDOM} << 15) + ${RANDOM}
. Ini secara signifikan mengurangi bias dan memungkinkannya bekerja untuk file yang berisi hingga 1 miliar baris.+
dan|
sama karena${RANDOM}
adalah 0..32767 menurut definisi.(Saya suka pendekatan shuf di atas bahkan lebih baik - saya bahkan tidak tahu itu ada dan saya tidak akan pernah menemukan alat itu sendiri)
sumber
sort
, tidak bekerja pada sistem saya (CentOS 5.5, Mac OS 10.7.2). Juga, penggunaan kucing yang tidak berguna, dapat dikurangi menjadisort --random-sort < $FILE | head -n 1
sort -R <<< $'1\n1\n2' | head -1
lebih mungkin untuk mengembalikan 1 dan 2, karenasort -R
memilah garis duplikat bersama. Hal yang sama berlaku untuksort -Ru
, karena menghapus garis duplikat.sort
sebelum dikirimhead
.shuf
memilih garis acak dari file, sebagai gantinya dan jauh lebih cepat bagi saya.sort --random-sort $FILE | head
akan lebih baik, karena memungkinkannya untuk mengakses file secara langsung, mungkin memungkinkan penyortiran paralel yang efisien--random-sort
dan-R
opsi khusus untuk GNU semacam (sehingga mereka tidak akan bekerja dengan BSD atau Mac OSsort
). GNU mengurutkannya pada tahun 2005 sehingga Anda membutuhkan GNU coreutils 6.0 atau yang lebih baru (mis. CentOS 6).Ini sederhana.
Memang ini hanya sedikit lebih lambat daripada "shuf -n 1 file.txt" sendiri.
sumber
-n 1
menentukan 1 baris, dan Anda dapat mengubahnya menjadi lebih dari 1.shuf
dapat digunakan untuk hal-hal lain juga; Saya baru saja menyalurkanps aux
dangrep
dengan itu untuk secara acak membunuh proses pencocokan sebagian nama.perlfaq5: Bagaimana cara memilih garis acak dari suatu file? Berikut algoritma pengambilan sampel reservoir dari Buku Unta:
Ini memiliki keuntungan yang signifikan dalam ruang dibandingkan membaca seluruh file. Anda dapat menemukan bukti metode ini di The Art of Computer Programming, Volume 2, Bagian 3.4.2, oleh Donald E. Knuth.
sumber
shuf
. Kode perl sangat sedikit lebih cepat (8% lebih cepat oleh waktu pengguna, 24% lebih cepat dengan waktu sistem), meskipun secara anekdot saya telah menemukan kode perl "tampaknya" kurang acak (saya menulis jukebox menggunakannya).shuf
menyimpan seluruh file input dalam memori , yang merupakan ide yang mengerikan, sementara kode ini hanya menyimpan satu baris, sehingga batas kode ini adalah jumlah baris INT_MAX (2 ^ 31 atau 2 ^ 63 tergantung pada Anda arch), dengan asumsi salah satu jalur potensial yang dipilih sesuai dengan memori.menggunakan skrip bash:
sumber
Garis bash tunggal:
Sedikit masalah: duplikat nama file.
sumber
wc -l < test.txt
menghindari harus pipa kecut
.Berikut skrip Python sederhana yang akan melakukan pekerjaan:
Pemakaian:
sumber
import random, sys lines = open(sys.argv[1]).readlines()
untuk saya dalam jangkauan (len (baris)): rand = random.randint (0, len (lines) -1) mencetak lines.pop (rand),len(lines)
dapat menyebabkan IndexError. Anda bisa menggunakannyaprint(random.choice(list(open(sys.argv[1]))))
. Ada juga algoritma pengambilan sampel reservoir efisien memori .Cara lain menggunakan ' awk '
sumber
$RANDOM
adalah bashism ). Berikut ini adalah metode awk (mawk) murni menggunakan logika yang sama dengan kode perlfaq5 yang dikutip oleh @ Tracker1 di atas:awk 'rand() * NR < 1 { line = $0 } END { print line }' file.name
(wow, ini bahkan lebih pendek dari kode perl!)wc
) untuk mendapatkan jumlah baris, kemudian harus membaca (bagian dari) file itu lagi (awk
) untuk mendapatkan konten dari nomor baris acak yang diberikan. I / O akan jauh lebih mahal daripada mendapatkan nomor acak. Kode saya hanya membaca file sekali. Masalah dengan awkrand()
adalah bahwa seed berdasarkan pada detik, sehingga Anda akan mendapatkan duplikat jika Anda menjalankannya terlalu cepat secara berurutan.Solusi yang juga berfungsi di MacOSX, dan seharusnya juga bekerja di Linux (?):
Dimana:
N
adalah jumlah garis acak yang Anda inginkanNR==FNR {lineN[$1]; next}(FNR in lineN) file1 file2
-> simpan nomor baris yang ditulisfile1
dan kemudian cetak baris yang sesuaifile2
jot -r $N 1 $(wc -l < $file)
-> menggambarN
angka secara acak (-r
) dalam kisaran(1, number_of_line_in_file)
denganjot
. Substitusi proses<()
akan membuatnya terlihat seperti file untuk penerjemah, jadifile1
pada contoh sebelumnya.sumber
sumber
Inilah yang saya temukan karena Mac OS saya tidak menggunakan semua jawaban mudah. Saya menggunakan perintah jot untuk menghasilkan angka karena solusi variabel $ RANDOM tampaknya tidak terlalu acak dalam pengujian saya. Saat menguji solusi saya, saya memiliki varian yang luas dalam solusi yang disediakan dalam output.
Gema variabel adalah untuk mendapatkan visual dari angka acak yang dihasilkan.
sumber
Hanya menggunakan vanilla sed dan awk, dan tanpa menggunakan $ RANDOM, "one-liner" sederhana, hemat ruang, dan cukup cepat untuk memilih satu baris pseudo-acak dari file bernama FILENAME adalah sebagai berikut:
(Ini berfungsi bahkan jika FILENAME kosong, dalam hal ini tidak ada garis yang dipancarkan.)
Satu keuntungan yang mungkin dari pendekatan ini adalah hanya memanggil rand () sekali.
Seperti yang ditunjukkan oleh @AdamKatz di komentar, kemungkinan lain adalah memanggil rand () untuk setiap baris:
(Bukti kebenaran sederhana dapat diberikan berdasarkan induksi.)
Peringatan tentang
rand()
"Di sebagian besar implementasi awk, termasuk gawk, rand () mulai menghasilkan angka dari nomor awal yang sama, atau seed, setiap kali Anda menjalankan awk."
- https://www.gnu.org/software/gawk/manual/html_node/Numeric-Functions.html
sumber