Como exibir uma linha aleatória de um arquivo de texto?

21

Estou tentando escrever um script de shell. A idéia é selecionar uma única linha aleatoriamente a partir do arquivo de texto e exibi-lo como uma notificação de desktop do Ubuntu.

Mas quero que linhas diferentes sejam selecionadas toda vez que eu executar o script. Existe alguma solução para fazer isso? Eu não quero o roteiro inteiro. Apenas isso simples.

    
por Anandu M Das 18.09.2014 / 15:12

6 respostas

33

Você pode usar o utilitário shuf para imprimir linhas aleatórias do arquivo

$ shuf -n 1 filename

-n : número de linhas a serem impressas

Exemplos:

$ 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
    
por aneeshep 18.09.2014 / 15:20
13

Você também pode usar o comando sort para obter uma linha aleatória do arquivo.

sort -R filename | head -n1
    
por g_p 18.09.2014 / 15:26
8

Apenas por diversão, aqui está uma solução bash pura que não usa shuf , sort , wc , sed , head , tail ou qualquer outras ferramentas externas.

A única vantagem sobre a variante shuf é que ela é um pouco mais rápida, já que é puro bash. Na minha máquina, para um arquivo de 1000 linhas, a variante shuf leva cerca de 0,1 segundos, enquanto o script a seguir leva em torno de 0,01 segundos;) Assim, enquanto shuf é a variante mais fácil e mais curta, isso é mais rápido.

Com toda a honestidade, eu ainda preferiria a solução shuf , a menos que a alta eficiência seja uma preocupação 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"
    
por Malte Skoruppa 18.09.2014 / 16:15
4

Digamos que você tenha o arquivo notifications.txt . Precisamos contar o número total de linhas, para determinar o alcance do gerador aleatório:

$ cat notifications.txt | wc -l

Permite escrever para a variável:

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

Agora, para gerar o número de 0 a $LINE , usaremos RANDOM variable.

$ echo $[ $RANDOM % LINES]

Vamos escrever para a variável:

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

Agora só precisamos imprimir esse número de linha:

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

Sobre a 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.

Certifique-se de que seu arquivo tenha menos de 32767 números de linha. Consulte this se precisar de gerador aleatório maior que funcione fora da caixa.

Exemplo:

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

Aqui está um script Python que seleciona uma linha aleatória dos arquivos de entrada ou 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

O algoritmo é O (n) -time, O (1) -space. Ele funciona para arquivos maiores que 32767 linhas. Não carrega arquivos de entrada na memória. Ele lê cada linha de entrada exatamente uma vez, isto é, você pode distribuir um conteúdo arbitrário grande (mas finito) nele. Aqui está uma explicação do algoritmo .

    
por jfs 24.09.2014 / 08:49
1

Estou impressionado com o trabalho que Malte Skoruppa e outros fizeram, mas aqui está uma maneira muito mais simples de "bash puro":

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

Como alguns notaram, $ RANDOM não é aleatório. No entanto, o limite de tamanho de arquivo de 32767 linhas é superado ao vincular $ RANDOMs, conforme necessário.

    
por Wastrel 22.01.2018 / 20:05