Unix komut satırındaki bir dosyadan rastgele satırı okumanın kolay yolu nedir?


Yanıtlar:


383

Ş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 shufbunun yerine kullanılmasını önerir (yaratıldığında mevcut değildi, inanıyorum). shufGNU coreutils'in bir parçası rl, değil.

rl -c 1 $FILE

2
Bahşiş için teşekkürler shuf, Fedora'da yerleşiktir.
Cheng

5
Andalso, sort -Rkesinlikle bir bekleyin yapacak çok 80kk hatları - -, oysa ölçüde büyük dosyalarla uğraşırken eğer shuf -noldukça anında görür.
Rubens

23
coreutilsHomebrew'dan kurarak OS X'te shuf alabilirsiniz . gshufBunun yerine çağrılabilir shuf.
Alyssa Ross

2
Benzer şekilde, randomize-linesOS X'tebrew install randomize-lines; rl -c 1 $FILE
Jamie

4
Bunun GNU Coreutils'in birshuf 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ı).
Adam Katz

74

Başka bir alternatif:

head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1

28
$ {RANDOM} yalnızca 32768'den küçük sayılar üretir, bu yüzden bunu büyük dosyalar için kullanmayın (örneğin İngilizce sözlük).
Ralf

3
Bu, modulo işlemi nedeniyle her satır için kesin olarak aynı olasılığı vermez. Dosya uzunluğunun << 32768 (ve bu sayıyı bölmesi hiç de değil), ancak belki de kayda değer.
Anaphory

10
Düğmesini kullanarak bunu 30 bit rasgele sayılara genişletebilirsiniz (${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.
nneonneo

@nneonneo: Bu linke göre o $ {RANDOM} OR'ing gerektiğini olsa çok yerine PLUS'ing ait s ', hile soğutmak stackoverflow.com/a/19602060/293064
Jay Taylor

+ve tanım gereği 0..32767 |olduğu için aynıdır ${RANDOM}.
nneonneo

71
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)


10
+1 Hoşuma gitti, ancak çok yeni bir sürüme ihtiyacınız olabilir 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
Steve Kehlet

sort -R <<< $'1\n1\n2' | head -1sort -Ryinelenen 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.
Lri

5
Bu nispeten yavaştır, çünkü tüm dosyaya bağlanmadan sortönce karıştırılması gerekir head. shufdosyadan rastgele satırlar seçer ve benim için çok daha hızlıdır.
Bengt

1
@SteveKehlet, dosyadayken sort --random-sort $FILE | headdoğrudan erişmesine izin verdiği için, muhtemelen verimli paralel sıralamayı mümkün
kıldığı

5
--random-sortVe -Rseç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).
RJHunter

31

Bu basit.

cat file.txt | shuf -n 1

Bu sadece "shuf -n 1 file.txt" den biraz daha yavaş verilmiş.


2
En iyi cevap. Bu komutu bilmiyordum. Not -n 11 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 auxve greponunla.
sudo

18

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.


1
Yalnızca içerme amacıyla (belirtilen sitenin çökmesi durumunda), Tracker1'in işaret ettiği kod: "cat dosyaadı | perl -e" (<>) {push (@ _, $ _);} print @ _ [rand () * '_]';"
Anirvan

3
Bu kedinin yararsız bir kullanımıdır. İşte perlfaq5'te (ve Deve kitabının izniyle) bulunan kodda küçük bir değişiklik var: perl -e 'srand; rand ($.) <1 && ($ line = $ _) ise <>; $ line yazdır; ' dosyaadı
Bay Muskrat

err ... bağlantılı site, yani
Nathan Fellman

Ben sadece bu kodun bir N-satır sürümünü karşılaştırdı 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).
Adam Katz

2
Düşünce için daha fazla yiyecek: shufgiriş 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.
Adam Katz

11

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}

1
Rastgele 0 olabilir, sed ilk satır için 1'e ihtiyaç duyar. sed -n 0p hata döndürür.
asalamon74

mhm - "tmp.txt" için $ 1 ve NUM için $ 2 ne dersiniz?
blabla999

ancak perl veya python'a ihtiyaç duymadığı ve alabileceğiniz kadar verimli olduğu için (bir dosyayı tam olarak iki kez okumak ama belleğe okumak değil - bu yüzden büyük dosyalarla bile çalışacaktır).
blabla999

@ asalamon74: teşekkürler @ blabla999: Eğer bir işlev yaparsak, 1 $ 'a tamam, ama neden NUM hesaplamıyorsunuz?
Paolo Tedesco

Sed satırını şu şekilde değiştirmek: head - $ {X} $ {FILE} | kuyruk -1 yapmalı
JeffK

4

Tek bash hattı:

sed -n $((1+$RANDOM%`wc -l test.txt | cut -f 1 -d ' '`))p test.txt

Hafif sorun: yinelenen dosya adı.


2
daha hafif bir problem. bunu / usr / share / dict / words üzerinde yapmak "A" ile başlayan kelimeleri tercih etme eğilimindedir. Onunla oynamak, yaklaşık% 90 "A" kelimeleri ile% 10 "B" kelimeleri arasındayım. Hiçbiri henüz sayılarla başlamaz, bu da dosyanın başını oluşturur.
bibby

wc -l < test.txtboru döşemek zorunda kalmaz cut.
fedorqui 'SO' zarar vermeyi kes '11

3

İş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

1
Bu pek işe yaramıyor. Tek bir satırdan sonra durur. Çalıştırmak için şunu yaptım: 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),
Jed Daniels

Crappy biçimlendirme ile aptalca yorum sistemi. Yorumlarda biçimlendirme bir zamanlar işe yaramadı mı?
Jed Daniels

Randint kapsayıcıdır, bu nedenle 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 .
jfs

2
Oldukça yer aç; 3 TB'lık bir dosya düşünün.
Michael Campbell

@MichaelCampbell: Yukarıda bahsettiğim rezervuar örnekleme algoritması 3TB dosyasıyla çalışabilir (satır boyutu sınırlıysa).
jfs

2

' Awk ' kullanmanın başka bir yolu

awk NR==$((${RANDOM} % `wc -l < file.name` + 1)) file.name

2
Bu awk ve bash kullanır ( $RANDOMbir 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!)
Adam Katz

Bu kod, wcbir satır sayısı elde etmek için dosyayı ( ) okumalı, daha sonra awkverilen 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.
Adam Katz

1

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 file1ve ardından ilgili satırıfile2

  • jot -r $N 1 $(wc -l < $file)-> çizmek Nrastgele (sayıları -raralığı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.

0
#!/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]}

$ RANDOM, / usr / share / dict / words içindeki kelime sayısından daha az sayı ürettiğinden 235886 (yine de Mac'imde), sadece 0 ile 9 arasında 6 ayrı rastgele sayı üretiyorum ve bunları bir araya getiriyorum. Sonra bu sayı 235886 daha az olduğundan emin olun. Sonra dizide sakladığım sözcükleri dizin için önde gelen sıfırları kaldırın. Her kelime kendi satırı olduğundan bu, herhangi bir dosyanın rastgele bir çizgi seçmesi için kolayca kullanılabilir.
Ken

0

İş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.


0

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.)

Hakkında uyarı 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


Sed gerektirmeyen daha basit bir awk çözümüne sahip olan bu cevaptan bir yıl önce gönderdiğim yoruma bakın . Ayrıca, tüm saniyelerde tohumlanan awk'ın rastgele sayı üreteci hakkındaki uyarımı not edin.
Adam Katz
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.