Unix komut satırındaki bir dosyadan rastgele satırı okumanın kolay yolu nedir?
Unix komut satırındaki bir dosyadan rastgele satırı okumanın kolay yolu nedir?
Yanıtlar:
Şunları kullanabilirsiniz shuf
:
shuf -n 1 $FILE
Ayrıca bir yardımcı program var rl
. Debian'da randomize-lines
, tüm dağıtımlarda mevcut olmasa da, tam olarak ne istediğinizi yapan pakette bulunur. Ana sayfasında aslında shuf
bunun yerine kullanılmasını önerir (yaratıldığında mevcut değildi, inanıyorum). shuf
GNU coreutils'in bir parçası rl
, değil.
rl -c 1 $FILE
shuf
, Fedora'da yerleşiktir.
sort -R
kesinlikle bir bekleyin yapacak çok 80kk hatları - -, oysa ölçüde büyük dosyalarla uğraşırken eğer shuf -n
oldukça anında görür.
coreutils
Homebrew'dan kurarak OS X'te shuf alabilirsiniz . gshuf
Bunun yerine çağrılabilir shuf
.
randomize-lines
OS X'tebrew install randomize-lines; rl -c 1 $FILE
shuf
parçası olduğunu ve bu nedenle (varsayılan olarak) * BSD sistemlerinde (veya Mac?) Kullanılamayacağını unutmayın. @ Tracker1'in perl tek astarı daha taşınabilir (ve testlerimden biraz daha hızlı).
Başka bir alternatif:
head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1
(${RANDOM} << 15) + ${RANDOM}
. Bu, sapmayı önemli ölçüde azaltır ve 1 milyar satıra kadar olan dosyalar için çalışmasına izin verir.
+
ve tanım gereği 0..32767 |
olduğu için aynıdır ${RANDOM}
.
sort --random-sort $FILE | head -n 1
(Yukarıdaki shuf yaklaşımını daha da seviyorum - bunun var olduğunu bile bilmiyordum ve bu aracı asla kendi başıma bulamazdım)
sort
, hiçbir sistemim üzerinde çalışmadı (CentOS 5.5, Mac OS 10.7.2). Ayrıca, kedinin yararsız kullanımı, azaltılabilirsort --random-sort < $FILE | head -n 1
sort -R <<< $'1\n1\n2' | head -1
sort -R
yinelenen satırları birlikte sıraladığından , 1 ve 2 değerini döndürme olasılığı yüksektir . Aynısı geçerlidir sort -Ru
, çünkü yinelenen satırları kaldırır.
sort
önce karıştırılması gerekir head
. shuf
dosyadan rastgele satırlar seçer ve benim için çok daha hızlıdır.
sort --random-sort $FILE | head
doğrudan erişmesine izin verdiği için, muhtemelen verimli paralel sıralamayı mümkün
--random-sort
Ve -R
seçenekleri GNU tür özgü (bunlar BSD veya Mac OS ile çalışma olmaz böylece sort
). GNU sıralama 2005 yılında bu bayrakları öğrendi, böylece GNU coreutils 6.0 veya daha yenisine ihtiyacınız var (örn. CentOS 6).
Bu basit.
cat file.txt | shuf -n 1
Bu sadece "shuf -n 1 file.txt" den biraz daha yavaş verilmiş.
-n 1
1 satır belirtir ve 1'den fazla olarak değiştirebilirsiniz shuf
. Başka şeyler için de kullanılabilir; Sadece bir adla eşleşen işlemleri rastgele öldürmek için piped ps aux
ve grep
onunla.
perlfaq5: Bir dosyadan rastgele bir çizgi nasıl seçerim? Deve Kitabı'ndan bir rezervuar örnekleme algoritması:
perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' file
Bu, tüm dosyayı okumaya göre uzayda önemli bir avantaja sahiptir. Bu yöntemin bir kanıtını Donald E. Knuth'un Bilgisayar Programlama Sanatı, Cilt 2, Bölüm 3.4.2'de bulabilirsiniz.
shuf
. Perl kodu çok daha hızlı (kullanıcı zamanına göre% 8 daha hızlı, sistem zamanına göre% 24 daha hızlı), anekdot olarak perl kodunu "daha az rasgele görünüyor" buldum (bunu kullanarak bir müzik kutusu yazdım).
shuf
giriş dosyasının tamamını bellekte saklar , bu korkunç bir fikirdir, bu kod sadece bir satır saklar, bu nedenle bu kodun sınırı INT_MAX satır sayısıdır (2 ^ 31 veya 2 ^ 63 kemer), seçilen potansiyel çizgilerinden herhangi birinin belleğe sığdığını varsayarsak.
bir bash betiği kullanarak:
#!/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}
Tek bash hattı:
sed -n $((1+$RANDOM%`wc -l test.txt | cut -f 1 -d ' '`))p test.txt
Hafif sorun: yinelenen dosya adı.
wc -l < test.txt
boru döşemek zorunda kalmaz cut
.
İşte işi yapacak basit bir Python betiği:
import random, sys
lines = open(sys.argv[1]).readlines()
print(lines[random.randrange(len(lines))])
Kullanımı:
python randline.py file_to_get_random_line_from
import random, sys lines = open(sys.argv[1]).readlines()
aralıktaki i için (len (satır)): rand = random.randint (0, len (satır) -1) print lines.pop (rand),
len(lines)
IndexError'a yol açabilir. Kullanabilirsin print(random.choice(list(open(sys.argv[1]))))
. Ayrıca bellek verimli rezervuar örnekleme algoritması da vardır .
' Awk ' kullanmanın başka bir yolu
awk NR==$((${RANDOM} % `wc -l < file.name` + 1)) file.name
$RANDOM
bir bashizmdir ). İşte yukarıda Tracker1 en anılan perlfaq5 kodu @ aynı mantık kullanılarak saf awk (mawk) yöntemidir: awk 'rand() * NR < 1 { line = $0 } END { print line }' file.name
(vay, hatta var kısa perl kodu daha!)
wc
bir satır sayısı elde etmek için dosyayı ( ) okumalı, daha sonra awk
verilen rasgele satır numarasının içeriğini almak için dosyayı (kısmını) tekrar okumalıdır () . I / O rastgele bir sayı almaktan çok daha pahalı olacaktır. Kodum dosyayı yalnızca bir kez okur. Awk ile ilgili sorun rand()
saniyeler içinde tohum olması, bu yüzden art arda çok hızlı çalıştırırsanız kopyaları alırsınız.
MacOSX üzerinde de çalışan ve Linux üzerinde de çalışması gereken bir çözüm (?):
N=5
awk 'NR==FNR {lineN[$1]; next}(FNR in lineN)' <(jot -r $N 1 $(wc -l < $file)) $file
Nerede:
N
istediğiniz rastgele satır sayısı
NR==FNR {lineN[$1]; next}(FNR in lineN) file1 file2
-> Yazılan satır numaralarını kaydedin file1
ve ardından ilgili satırıfile2
jot -r $N 1 $(wc -l < $file)
-> çizmek N
rastgele (sayıları -r
aralığında) (1, number_of_line_in_file)
ile jot
. Süreç ikamesi <()
, tercüman için bir dosya gibi görünmesini sağlayacaktır, bu nedenle file1
önceki örnekte.#!/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]}
İşte Mac OS'm tüm kolay cevapları kullanmadığı için keşfettiğim şey. $ RANDOM değişken çözümleri benim test çok rasgele görünmüyor çünkü bir sayı oluşturmak için jot komutunu kullandım. Çözümümü test ederken, çıktıda sağlanan çözümlerde büyük bir fark vardı.
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
Değişkenin yankısı, üretilen rastgele sayının bir görselini elde etmektir.
Yalnızca vanilya sed ve awk kullanarak ve $ RANDOM kullanmadan, FILENAME adlı bir dosyadan rasgele tek bir satır seçmek için basit, yerden tasarruf sağlayan ve oldukça hızlı bir "tek astar" aşağıdaki gibidir:
sed -n $(awk 'END {srand(); r=rand()*NR; if (r<NR) {sub(/\..*/,"",r); r++;}; print r}' FILENAME)p FILENAME
(Bu, FILENAME boş olsa bile çalışır, bu durumda hiçbir satır yayınlanmaz.)
Bu yaklaşımın olası bir avantajı, rand () işlevini yalnızca bir kez çağırmasıdır.
Yorumlarda @AdamKatz tarafından işaret edildiği gibi, her bir satır için rand () öğesini çağırmak başka bir olasılık olacaktır:
awk 'rand() * NR < 1 { line = $0 } END { print line }' FILENAME
(İndüksiyon temelinde basit bir doğruluk kanıtı verilebilir.)
rand()
"Gawk dahil olmak üzere çoğu awk uygulamasında, rand () her awk çalıştırdığınızda aynı başlangıç numarasından veya tohumdan numaralar üretmeye başlar."
- https://www.gnu.org/software/gawk/manual/html_node/Numeric-Functions.html