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.
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
Vous pouvez également utiliser la commande sort
pour obtenir une ligne aléatoire du fichier.
sort -R filename | head -n1
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"
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 ' '
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 .
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.