Bir metin dosyasından rastgele bir çizgi nasıl görüntülenir?


26

Bir kabuk betiği yazmaya çalışıyorum. Fikir metin dosyasından rastgele tek bir satır seçmek ve bir Ubuntu masaüstü bildirimi olarak görüntülemek.

Ancak senaryoyu her çalıştırdığımda farklı satırların seçilmesini istiyorum. Bunu yapmak için herhangi bir çözüm var mı? Komut dosyasının tamamını istemiyorum. Sadece bu basit şey.


Ayrıca ziyaret edin: askubuntu.com/q/492572/256099
Pandya

Yanıtlar:


40

shufDosyadan rasgele çizgiler yazdırmak için yardımcı programını kullanabilirsiniz

$ shuf -n 1 filename

-n : yazdırılacak satır sayısı

Örnekler:

$ shuf -n 1 /etc/passwd

git:x:998:998:git daemon user:/:/bin/bash

$ shuf -n 2 /etc/passwd

avahi:x:84:84:avahi:/:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false

Fakat bunu kullanarak n'nin değerini manuel olarak doğru değiştirmem gerekiyor. Bu kabuğun otomatik olarak rasgele başka bir çizgi seçmesini istiyorum. Tam olarak rastgele olması gerekmez. Ama başka bir çizgi.
Anandu M Das

4
@AnanduMDas Hayır n, yazdırılacak satır sayısını belirtmeniz gerekmez . (yani sadece bir satır mı yoksa iki satır mı istediğinizi). Satır numarası değil (yani birinci satır 2. satır).
aneeshep 18:14

@AnanduMDas: Cevabıma bazı örnekler ekledim. Umarım şimdi açıktır.
aneeshep

1
Şimdi net teşekkür ederim :) Ayrıca, başka bir algoritma da buldum, onun gibi, şimdiki zamanı (sadece ikinci, by date +%S) x değişkenine kaydeder ve sonra metin dosyasındaki headve tailkomutlarını kullanarak bu xth satırını seçerim . Neyse, yöntemin daha kolay. Teşekkürler
Anandu M Das

+1: shufcoreutils'te olduğundan varsayılan olarak kullanılabilir. Not: Giriş dosyasını belleğe yükler. Bunu gerektirmeyen verimli bir algoritma var .
jfs


8

Sadece eğlence için, burada bir olan saf bash çözümü kullanmaz shuf, sort, wc, sed, head, tailveya herhangi bir diğer harici araçları.

shufDeğişkene göre tek avantajı saf bash olduğundan biraz daha hızlı olmasıdır. Makinemde, 1000 satırlık bir dosya için shufdeğişken yaklaşık 0,1 saniye sürüyor, aşağıdaki komut dosyası yaklaşık 0,01 saniye sürüyor;) shufEn kolay ve en kısa değişken olsa bile, bu daha hızlı.

Tüm dürüstlükte shuf, yüksek verimlilik önemli bir endişe olmadıkça hala çözüme giderdim .

#!/bin/bash

FILE=file.txt

# get line count for $FILE (simulate 'wc -l')
lc=0
while read -r line; do
 ((lc++))
done < $FILE

# get a random number between 1 and $lc
rnd=$RANDOM
let "rnd %= $lc"
((rnd++))

# traverse file and find line number $rnd
i=0
while read -r line; do
 ((i++))
 [ $i -eq $rnd ] && break
done < $FILE

# output random line
printf '%s\n' "$line"

@EliahKagan Önerileriniz ve puanınız için teşekkür ederiz. Kabul etmiyorum ki, gerçekten çok fazla düşünmedim, çok az sayıda köşe davası var. Bunu eğlenmek için daha çok yazdım. shufZaten kullanmak çok daha iyi. Bunu düşünerek, saf bash'in daha shufönce yazdığım gibi aslında kullanmaktan daha verimli olduğuna inanmıyorum . Harici bir aleti ateşlerken en ufak (sabit) tepegöz olabilir, ancak daha sonra yorumlanan bash'ten daha hızlı çalışacaktır. Bu yüzden shufkesinlikle daha iyi ölçekler. Diyelim ki senaryo bir eğitim amacına hizmet ediyor: Yapılabileceğini görmek güzel;)
Malte Skoruppa

GNU / Linux / Un * x, tamamen akademik bir egzersiz olmadıkça, yeniden icat etmek istemediğim çok iyi yol testli jantlara sahiptir. "Kabuk", çeşitli şekillerde giriş / çıkış ve bol miktarda 'seçenekleriyle monte edilebilecek (mevcut) çok sayıda küçük parçanın birleştirilmesinde kullanılmak üzere tasarlanmıştır. Başka bir şey kötü değil, eğer spor için değilse (örneğin, codegolf.stackexchange.com/tour ), bu durumda, oynayın ...!
michael

2
@michael_n Her ne kadar "saf bash" yolu, öğretmek ve diğer görevler için değişiklik yapmak için çoğunlukla yararlı olsa da, bu göründüğünden daha gerçek bir "gerçek" uygulamasıdır. Bash yaygın olarak bulunur ancak shufGNU Coreutils'e özgüdür (örneğin, FreeBSD 10.0'da değil). sort -Rportatiftir ancak farklı (ilişkili) bir sorunu çözer: çoklu çizgiler olarak görünen dizgilerin yalnızca bir kez görünenlere eşit olasılıkları vardır. (Tabii ki, wcve diğer kamu hizmetleri hala kullanılabilir.) Bence buradaki ana sınırlama, bunun 32768'inci satırdan sonra hiçbir şey seçmemesi (ve daha az rastlantısal hale gelmesi).
Eliah Kagan

2
Malte Skoruppa: Bash PRNG sorusunu U&L'ye taşıdığınızı görüyorum . Güzel. İpucu: $((RANDOM<<15|RANDOM))0,2 ^ 30-1 arasındadır. @JFSebastian Daha sık girilen girişlere doğru eğimli shufdeğil sort -R. Put shuf -n 1yerine sort -R | head -n1ve karşılaştırın. (Btw 10 ^ 3 yinelemeler 10 ^ 6'dan daha hızlıdır ve farkı göstermek için hala oldukça yeterlidir.) Ayrıca daha sert, daha görsel bir demo ve tüm dizelerin yüksek frekanslı olduğu büyük girdilerde çalıştığını gösteren bu saçma sapanlığa bakın .
Eliah Kagan

1
@ JFSebastian Bu komutta, girdi diehardertamamen sıfır gibi görünüyor. Bunun benim açımdan sadece garip bir hata olmadığını varsayarsak, bunun neden rastgele olmadığını açıklar! while echo $(( RANDOM << 17 | RANDOM << 2 | RANDOM >> 13 )); do :; done | perl -ne 'print pack "I>"' > outBir süre çalışırsanız ve daha sonra outhex editörünün içeriğini incelerseniz iyi görünümlü veriler elde eder misiniz ? (Ya ancak başka benzeri görüntülemek.) Bütün sıfırları almak ve RANDOMsuçlu değil: Ben yerine zaman bütün sıfırları almak $(( RANDOM << 17 | RANDOM << 2 | RANDOM >> 13 ))ile 100de.
Eliah Kagan

4

Dosyan olduğunu söyle notifications.txt. Rastgele oluşturucu aralığını belirlemek için toplam satır sayısını saymamız gerekir:

$ cat notifications.txt | wc -l

Değişkene yazalım:

$ LINES=$(cat notifications.txt | wc -l)

Şimdi gelen numarayı oluşturmak için 0için $LINEbiz kullanacağız RANDOMdeğişkeni.

$ echo $[ $RANDOM % LINES]

Değişkene yazalım:

$  R_LINE=$(($RANDOM % LINES))

Şimdi sadece bu satır numarasını yazdırmamız gerekiyor:

$ sed -n "${R_LINE}p" notifications.txt

RANDOM hakkında:

   RANDOM Each time this parameter is referenced, a random integer between
          0 and 32767 is generated.  The sequence of random numbers may be
          initialized by assigning a value to RANDOM.  If RANDOM is unset,
          it  loses  its  special  properties,  even if it is subsequently
          reset.

Dosyanızın 32767'den az satır numarasına sahip olduğundan emin olun. Bkz bu kutunun dışında çalışır büyük rastgele jeneratörü gerekiyorsa.

Örnek:

$ od -A n -t d -N 3 /dev/urandom | tr -d ' '

Bir stilistik alternatif (bash):LINES=$(wc -l < file.txt); R_LINE=$((RANDOM % LINES)); sed -n "${R_LINE}p" file.txt
michael


Örneğin, rastgele bir sayıya uygulamanın neden iyi bir fikir olmadığını anlamak için gri bitmap kullanarak Test PRNG'deki son resme bakın % n.
jfs

2

İşte giriş dosyalarından veya stdin'den rastgele bir satır seçen bir Python betiği:

#!/usr/bin/env python
"""Usage: select-random [<file>]..."""
import random

def select_random(iterable, default=None, random=random):
    """Select a random element from iterable.

    Return default if iterable is empty.
    If iterable is a sequence then random.choice() is used for efficiency instead.
    If iterable is an iterator; it is exhausted.
    O(n)-time, O(1)-space algorithm.
    """
    try:
        return random.choice(iterable) # O(1) time and space
    except IndexError: # empty sequence
        return default
    except TypeError: # not a sequence
        return select_random_it(iter(iterable), default, random.randrange)

def select_random_it(iterator, default=None, randrange=random.randrange):
    """Return a random element from iterator.

    Return default if iterator is empty.
    iterator is exhausted.
    O(n)-time, O(1)-space algorithm.
    """
    # from /programming//a/1456750/4279
    # select 1st item with probability 100% (if input is one item, return it)
    # select 2nd item with probability 50% (or 50% the selection stays the 1st)
    # select 3rd item with probability 33.(3)%
    # select nth item with probability 1/n
    selection = default
    for i, item in enumerate(iterator, start=1):
        if randrange(i) == 0: # random [0..i)
            selection = item
    return selection

if __name__ == "__main__":
    import fileinput
    import sys

    random_line = select_random_it(fileinput.input(), '\n')
    sys.stdout.write(random_line)
    if not random_line.endswith('\n'):
        sys.stdout.write('\n') # always append newline at the end

Algoritma, O (n) -time, O (1) -space'dir. 32767 satırdan büyük dosyalar için çalışır. Giriş dosyalarını belleğe yüklemiyor. Her giriş satırını tam olarak bir kez okur; yani, isteğe bağlı olarak büyük (ancak sonlu) içeriği, içine yerleştirebilirsiniz. İşte algoritmanın bir açıklaması .


1

Malte Skoruppa ve diğerlerinin yaptığı çalışmalardan etkilendim, ama işte bunu yapmanın çok daha basit bir "saf bash" yolu:

IFS=$'\012'
# set field separator to newline only
lines=( $(<test5) )
# slurp entire file into an array
numlines=${#lines[@]}
# count the array elements
num=$(( $RANDOM$RANDOM$RANDOM % numlines ))
# get a (more-or-less) random number within the correct range
line=${lines[$num]}
# select the element corresponding to the random number
echo $line
# display it

Bazılarının belirttiği gibi, $ RANDOM rastgele değildir. Ancak, 32767 satırlık dosya boyutu sınırı, $ RANDOM'ları gerektiği gibi bir araya getirerek aşılır.

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.