Apa cara mudah untuk membaca baris acak dari file di baris perintah Unix?
Apa cara mudah untuk membaca baris acak dari file di baris perintah Unix?
Jawaban:
Anda bisa menggunakan shuf:
shuf -n 1 $FILE
Ada juga utilitas yang disebut rl. Di Debian ada dalam randomize-linespaket yang melakukan persis apa yang Anda inginkan, meskipun tidak tersedia di semua distro. Di halaman beranda sebenarnya merekomendasikan penggunaan shufsebagai gantinya (yang tidak ada saat itu dibuat, saya percaya). shufadalah bagian dari GNU coreutils, rlbukan.
rl -c 1 $FILE
shuftipnya, ini built-in di Fedora.
sort -Rpasti akan membuat orang menunggu banyak jika berurusan dengan file yang sangat besar - 80kb baris -, sedangkan, shuf -nbertindak cukup instan.
coreutilsdari Homebrew. Mungkin bisa disebut gshufbukan shuf.
randomize-linesOS X olehbrew install randomize-lines; rl -c 1 $FILE
shufini 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:
head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1
(${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.
sort --random-sort $FILE | head -n 1
(Saya suka pendekatan shuf di atas bahkan lebih baik - saya bahkan tidak tahu itu ada dan saya tidak akan pernah menemukan alat itu sendiri)
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 -1lebih mungkin untuk mengembalikan 1 dan 2, karena sort -Rmemilah garis duplikat bersama. Hal yang sama berlaku untuk sort -Ru, karena menghapus garis duplikat.
sortsebelum dikirim head. shufmemilih garis acak dari file, sebagai gantinya dan jauh lebih cepat bagi saya.
sort --random-sort $FILE | headakan lebih baik, karena memungkinkannya untuk mengakses file secara langsung, mungkin memungkinkan penyortiran paralel yang efisien
--random-sortdan -Ropsi khusus untuk GNU semacam (sehingga mereka tidak akan bekerja dengan BSD atau Mac OS sort). GNU mengurutkannya pada tahun 2005 sehingga Anda membutuhkan GNU coreutils 6.0 atau yang lebih baru (mis. CentOS 6).
Ini sederhana.
cat file.txt | shuf -n 1
Memang ini hanya sedikit lebih lambat daripada "shuf -n 1 file.txt" sendiri.
-n 1menentukan 1 baris, dan Anda dapat mengubahnya menjadi lebih dari 1. shufdapat digunakan untuk hal-hal lain juga; Saya baru saja menyalurkan ps auxdan grepdengan 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:
perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' file
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.
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).
shufmenyimpan 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:
#!/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}
Garis bash tunggal:
sed -n $((1+$RANDOM%`wc -l test.txt | cut -f 1 -d ' '`))p test.txt
Sedikit masalah: duplikat nama file.
wc -l < test.txtmenghindari harus pipa ke cut.
Berikut skrip Python sederhana yang akan melakukan pekerjaan:
import random, sys
lines = open(sys.argv[1]).readlines()
print(lines[random.randrange(len(lines))])
Pemakaian:
python randline.py file_to_get_random_line_from
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 menggunakannya print(random.choice(list(open(sys.argv[1])))). Ada juga algoritma pengambilan sampel reservoir efisien memori .
Cara lain menggunakan ' awk '
awk NR==$((${RANDOM} % `wc -l < file.name` + 1)) file.name
$RANDOMadalah 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 awk rand()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 (?):
N=5
awk 'NR==FNR {lineN[$1]; next}(FNR in lineN)' <(jot -r $N 1 $(wc -l < $file)) $file
Dimana:
N adalah jumlah garis acak yang Anda inginkan
NR==FNR {lineN[$1]; next}(FNR in lineN) file1 file2
-> simpan nomor baris yang ditulis file1dan kemudian cetak baris yang sesuaifile2
jot -r $N 1 $(wc -l < $file)-> menggambar Nangka secara acak ( -r) dalam kisaran (1, number_of_line_in_file)dengan jot. Substitusi proses <()akan membuatnya terlihat seperti file untuk penerjemah, jadi file1pada contoh sebelumnya.#!/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]}
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.
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
Gema variabel adalah untuk mendapatkan visual dari angka acak yang dihasilkan.
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:
sed -n $(awk 'END {srand(); r=rand()*NR; if (r<NR) {sub(/\..*/,"",r); r++;}; print r}' FILENAME)p FILENAME
(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:
awk 'rand() * NR < 1 { line = $0 } END { print line }' FILENAME
(Bukti kebenaran sederhana dapat diberikan berdasarkan induksi.)
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