Come visualizzare una riga a caso da un file di testo?

20

Sto provando a scrivere uno script di shell. L'idea è di selezionare una singola riga a caso dal file di testo e visualizzarla come notifica desktop di Ubuntu.

Ma voglio che vengano selezionate linee diverse ogni volta che eseguo lo script. C'è qualche soluzione per fare questo? Non voglio l'intero copione. Solo questa semplice cosa.

    
posta Anandu M Das 18.09.2014 - 15:12

6 risposte

30

Puoi utilizzare l'utilità shuf per stampare le righe casuali dal file

$ shuf -n 1 filename

-n : numero di righe da stampare

Esempi:

$ 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
    
risposta data aneeshep 18.09.2014 - 15:20
13

Puoi anche utilizzare il comando sort per ottenere una riga casuale dal file.

sort -R filename | head -n1
    
risposta data g_p 18.09.2014 - 15:26
8

Solo per divertimento, ecco una soluzione bash pura che non usa shuf , sort , wc , sed , head , tail o qualsiasi altri strumenti esterni.

L'unico vantaggio rispetto alla variante shuf è che è leggermente più veloce, dal momento che è pura bash. Sulla mia macchina, per un file di 1000 righe la variante shuf richiede circa 0,1 secondi, mentre il seguente script impiega circa 0,01 secondi;) Quindi mentre shuf è la variante più semplice e più corta, questo è più veloce.

In tutta onestà, opterei per la soluzione shuf , a meno che l'alta efficienza non sia una preoccupazione importante.

#!/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"
    
risposta data Malte Skoruppa 18.09.2014 - 16:15
3

Diciamo che hai il file notifications.txt . Abbiamo bisogno di contare il numero totale di linee, per determinare l'intervallo del generatore casuale:

$ cat notifications.txt | wc -l

Consente di scrivere sulla variabile:

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

Ora per generare il numero da 0 a $LINE useremo RANDOM variabile.

$ echo $[ $RANDOM % LINES]

Consente di scrivere sulla variabile:

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

Ora abbiamo solo bisogno di stampare questo numero di riga:

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

Informazioni su RANDOM:

   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.

Assicurati che il tuo file abbia meno di 32767 numeri di riga. Vedi questo se hai bisogno di un generatore casuale più grande che funzioni fuori dalla scatola.

Esempio:

$ od -A n -t d -N 3 /dev/urandom | tr -d ' '
    
risposta data c0rp 18.09.2014 - 16:01
2

Ecco uno script Python che seleziona una riga casuale da file di input o stdin:

#!/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 https://stackoverflow.com/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

L'algoritmo è O (n) -time, O (1) -space. Funziona per file più grandi di 32767 linee. Non carica i file di input in memoria. Legge ogni riga di input esattamente una volta, cioè è possibile reindirizzare contenuti arbitrari di grandi dimensioni (ma limitati). Ecco una spiegazione dell'algoritmo .

    
risposta data jfs 24.09.2014 - 08:49
1

Sono impressionato dal lavoro fatto da Malte Skoruppa e altri, ma qui c'è un modo molto semplice di "puro bash" per farlo:

IFS=$'2'
# 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

Come alcuni hanno notato, $ RANDOM non è casuale. Tuttavia, il limite della dimensione del file di 32767 righe viene superato mettendo insieme le stringhe $ RANDOM in base alle esigenze.

    
risposta data Wastrel 22.01.2018 - 20:05

Leggi altre domande sui tag