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-lines
paket yang melakukan persis apa yang Anda inginkan, meskipun tidak tersedia di semua distro. Di halaman beranda sebenarnya merekomendasikan penggunaan shuf
sebagai gantinya (yang tidak ada saat itu dibuat, saya percaya). shuf
adalah bagian dari GNU coreutils, rl
bukan.
rl -c 1 $FILE
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 disebut gshuf
bukan shuf
.
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:
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 -1
lebih mungkin untuk mengembalikan 1 dan 2, karena sort -R
memilah garis duplikat bersama. Hal yang sama berlaku untuk sort -Ru
, karena menghapus garis duplikat.
sort
sebelum dikirim head
. 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 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 1
menentukan 1 baris, dan Anda dapat mengubahnya menjadi lebih dari 1. shuf
dapat digunakan untuk hal-hal lain juga; Saya baru saja menyalurkan ps aux
dan grep
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:
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).
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:
#!/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.txt
menghindari 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
$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 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 file1
dan kemudian cetak baris yang sesuaifile2
jot -r $N 1 $(wc -l < $file)
-> menggambar N
angka secara acak ( -r
) dalam kisaran (1, number_of_line_in_file)
dengan jot
. Substitusi proses <()
akan membuatnya terlihat seperti file untuk penerjemah, jadi file1
pada 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