web-dev-qa-db-fra.com

Comment afficher une ligne au hasard à partir d'un fichier texte?

J'essaie d'écrire un script shell. L'idée est de sélectionner une seule ligne au hasard dans un fichier texte et de l'afficher comme une notification du bureau Ubuntu.

Mais je veux que différentes lignes soient sélectionnées chaque fois que j'exécute le script. Y a-t-il une solution pour faire cela? Je ne veux pas le script entier. Juste cette chose simple seulement.

23
Anandu M Das

Vous pouvez utiliser l'utilitaire shuf pour imprimer des lignes aléatoires à partir d'un fichier.

$ shuf -n 1 filename

-n: nombre de lignes à imprimer

Exemples:

$ 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
37
aneeshep

Vous pouvez également utiliser la commande sort pour obtenir une ligne aléatoire du fichier.

sort -R filename | head -n1
13
g_p

Juste pour le plaisir, voici une solution pure bash qui n'utilise pas shuf, sort, wc, sed, head, tail ou tout autre outil externe.

Le seul avantage de la variante shuf est qu’elle est légèrement plus rapide, car elle est pure bash. Sur ma machine, pour un fichier de 1000 lignes, la variante shuf nécessite environ 0,1 seconde, tandis que le script suivant prend environ 0,01 seconde;) Ainsi, si shuf est la variante la plus simple et la plus courte, elle est plus rapide.

En toute honnêteté, je choisirais toujours la solution shuf, à moins que le rendement élevé ne soit une préoccupation 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"
8
Malte Skoruppa

Disons que vous avez le fichier notifications.txt. Nous devons compter le nombre total de lignes, afin de déterminer la plage du générateur aléatoire:

$ cat notifications.txt | wc -l

Permet d'écrire dans variable:

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

Maintenant, pour générer un nombre de 0 à $LINE, nous allons utiliser la variable RANDOM.

$ echo $[ $RANDOM % LINES]

Permet de l'écrire dans la variable:

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

Il ne nous reste plus qu'à imprimer ce numéro de ligne:

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

À propos de 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.

Assurez-vous que votre fichier contient moins de 32767 numéros de ligne. Voir this si vous avez besoin d’un plus grand générateur aléatoire qui fonctionne hors de la boîte.

Exemple:

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

Voici un script Python qui sélectionne une ligne aléatoire dans les fichiers d'entrée 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 __== "__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'algorithme est l'heure O (n), l'espace O (1). Cela fonctionne pour les fichiers de plus de 32767 lignes. Il ne charge pas les fichiers d'entrée en mémoire. Il lit chaque ligne d’entrée exactement une fois, c’est-à-dire que vous pouvez y insérer du contenu volumineux (mais fini). Voici un explication de l'algorithme .

2
jfs

Je suis impressionné par le travail réalisé par Malte Skoruppa et d'autres, mais voici une façon beaucoup plus simple de procéder de manière pure:

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

Comme certains l’ont noté, $ RANDOM n’est pas aléatoire. Toutefois, la limite de taille de fichier de 32 767 lignes est surmontée en enchaînant les $ RANDOM ensemble selon les besoins.

1
Wastrel