Comment générer un nombre aléatoire dans Bash?

236

Comment générer un nombre aléatoire dans une plage dans Bash?

woakas
la source
21
Comment doit-il être aléatoire?
bdonlan

Réponses:

283

Utilisez $RANDOM. Il est souvent utile en combinaison avec une simple arithmétique de coque. Par exemple, pour générer un nombre aléatoire entre 1 et 10 (inclus):

$ echo $((1 + RANDOM % 10))
3

Le générateur réel est dans variables.cla fonction brand(). Les anciennes versions étaient un simple générateur linéaire. La version 4.0 bashutilise un générateur avec une citation d'un article de 1985, ce qui signifie probablement que c'est une source décente de nombres pseudo-aléatoires. Je ne l'utiliserais pas pour une simulation (et certainement pas pour la cryptographie), mais c'est probablement suffisant pour les tâches de script de base.

Si vous faites quelque chose qui nécessite des nombres aléatoires sérieux, vous pouvez les utiliser /dev/randomou /dev/urandoms'ils sont disponibles:

$ dd if=/dev/urandom count=4 bs=1 | od -t d
Nelson
la source
21
Faites attention ici. Bien que cela soit bien dans un pincement, faire de l'arithmétique sur des nombres aléatoires peut considérablement affecter le caractère aléatoire de votre résultat. dans le cas de $RANDOM % 10, 8 et 9 sont mesurables (bien que marginalement) moins probables que 0-7, même s'il $RANDOMs'agit d'une source robuste de données aléatoires.
dimo414
3
@ dimo414 Je suis curieux de "marginalement", avez-vous une source où je peux en savoir plus à ce sujet?
PascalVKooten
58
En modulant vos entrées aléatoires, vous « collez » les résultats. Puisque $RANDOMla plage est constituée de 0-32767nombres 0- 7mappez sur 3277différentes entrées possibles, mais 8et 9ne pouvez être produites que de 3276différentes manières (car 32768et 32769ne sont pas possibles). Il s'agit d'un problème mineur pour les hacks rapides, mais cela signifie que le résultat n'est pas uniformément aléatoire. Les bibliothèques aléatoires, comme Java Random, offrent des fonctions pour renvoyer correctement un nombre aléatoire uniforme dans la plage donnée, plutôt que de simplement modifier un nombre non divisible.
dimo414
1
@JFSebastian très vrai - le problème avec modulo est qu'il peut casser l'uniformité de n'importe quel RNG, pas seulement de mauvais PRNG, mais merci de l'avoir appelé.
dimo414
14
Juste pour le contexte, le pigeonholing de base pour% 10 signifie que 8 et 9 sont environ 0,03% moins susceptibles de se produire que 0–7. Si votre script shell nécessite des nombres aléatoires uniformes plus précis que cela, alors utilisez certainement un mécanisme plus complexe et approprié.
Nelson
71

Veuillez voir $RANDOM:

$RANDOM est une fonction Bash interne (pas une constante) qui renvoie un entier pseudo-aléatoire dans la plage 0 - 32767. Elle ne doit pas être utilisée pour générer une clé de chiffrement.

Andrew Hare
la source
1
A 32767une signification particulière?
Jin Kwon
14
@JinKwon 32767est 2^16 / 2 - 1la limite supérieure d'un entier signé de 16 bits.
Jeffrey Martinez
@JinKwon pourriez-vous clarifier pourquoi ne dites-vous pas que c'est le cas 2^15 - 1? C'est équivalent, donc je suis juste curieux de savoir s'il y a un contexte qui me manque?
Brett Holman
11
@BrettHolman Je pense qu'il essayait de souligner la partie "signée" de l'entier 16 bits signé. 2 ^ 16 valeurs, divisées en deux pour les entiers positifs et négatifs.
cody
Cette réponse ne répond pas à la question.
morveux
48

Vous pouvez également utiliser shuf (disponible dans coreutils).

shuf -i 1-100000 -n 1
knipwim
la source
Comment passez-vous en vars en fin de gamme? J'ai eu ceci:shuf -i 1-10 -n 1: syntax error in expression (error token is "1-10 -n 1")
dat tutbrus
1
Ajoutez un $varau lieu de la fin de la plage, comme ceci:var=100 && shuf -i 1-${var} -n 1
knipwim
2
Je préfère cette option car il est facile de générer N nombres aléatoires avec -n. Par exemple, générer 5 nombres aléatoires entre 1 et 100 :shuf -i 1-100 -n 5
aerijman
Autant que je sache, les chiffres ne sont pas aléatoires. Si vous spécifiez, shuf -i 1-10 -n 10vous obtiendrez tous les nombres de 1 à 10 exactement. Si vous spécifiez, -n 15vous n'obtiendrez toujours qu'une seule fois ces 10 numéros. Ce n'est que du brassage, pas de génération de nombres aléatoires.
radlan le
Pour obtenir des nombres aléatoires avec remplacement: -r
Geoffrey Anderson
36

Essayez ceci depuis votre shell:

$ od -A n -t d -N 1 /dev/urandom

Ici, -t dspécifie que le format de sortie doit être signé décimal; -N 1dit de lire un octet de /dev/urandom.

Barun
la source
2
La question demande des nombres dans une plage.
JSycamore
9
vous pouvez supprimer des espaces:od -A n -t d -N 1 /dev/urandom |tr -d ' '
Robert
23

vous pouvez également obtenir un nombre aléatoire de awk

awk 'BEGIN {
   # seed
   srand()
   for (i=1;i<=1000;i++){
     print int(1 + rand() * 100)
   }
}'
ghostdog74
la source
1
+1 vous savez, au début, je me demandais pourquoi voudriez-vous jamais le faire comme ça, mais en fait je l'aime bien.
zelanix
1
Merci d'avoir donné une solution qui inclut l'ensemencement. Je ne l'ai trouvé nulle part!
so.very.tired
2
+1 pour l'ensemencement. Il peut être utile de mentionner que srand()le germe est le temps CPU actuel. Si vous devez spécifier une graine spécifique, afin que RNG puisse être dupliqué, utilisez srand(x)xest la graine. Également, cité dans le manuel des fonctions numériques de GNU awk, «différentes implémentations awk utilisent différents générateurs de nombres aléatoires en interne». Le résultat est que si vous souhaitez générer une distribution statistique, vous devez vous attendre à de légères variations allant d'un runtime à l'autre sur une plateforme différente (toutes en cours d'exécution awkou gawk).
Cbhihe
18

Il y a $ RANDOM. Je ne sais pas exactement comment ça marche. Mais ça marche. Pour les tests, vous pouvez faire:

echo $RANDOM
Antoine Claval
la source
16

J'aime cette astuce:

echo ${RANDOM:0:1} # random number between 1 and 9
echo ${RANDOM:0:2} # random number between 1 and 99

...

bouffon
la source
7
$ RANDOM est compris entre 0 et 32 ​​767. Vous obtiendrez plus de nombres commençant par 1, 2 ou 3 que vous n'en avez 4-9. Si vous êtes d'accord avec une distribution déséquilibrée, cela fonctionnera bien.
jbo5112
2
@ jbo5112 vous avez tout à fait raison, qu'en est-il de l'affichage du dernier chiffre? echo $ {RANDOM: 0-1} pour un chiffre, $ {RANDOM: 0-2} pour deux chiffres ...?
fraff
4
Si vous utilisez le (s) dernier (s) chiffre (s), ce sera plutôt bien, mais il inclura des 0 et des 00. Sur un seul chiffre, 0-7 se produira 0,03% plus souvent que 8-9. Sur 2 chiffres, 0-67 se produira 0,3% plus souvent que 68-99. Si vous avez besoin d'une distribution de nombres aléatoires aussi bonne, j'espère que vous n'utilisez pas bash. Avec l'original: ${RANDOM:0:1}a 67,8% de chances de vous donner un 1 ou un 2, ${RANDOM:0:2}n'a que 0,03% de chances de vous donner un numéro à un chiffre (devrait être de 1%), et les deux ont 0,003% de chances de vous donner un 0 Il existe encore des cas d'utilisation où cela est correct (par exemple, entrée non cohérente).
jbo5112
12

Nombre aléatoire entre 0 et 9 inclus.

echo $((RANDOM%10))
David Newcomb
la source
2
Mon mauvais, n'a pas lu la page de manuel correctement. $RANDOMne va que de 0 à 32767. Il aurait dû dire "Nombre aléatoire principalement entre 1 et 3, avec quelques ailiers";)
David Newcomb
1
Quoi? Ce sera toujours entre 0 et 9, bien que 8 et 9 auront une probabilité légèrement inférieure de se produire de 0 à 7, comme mentionné dans une autre réponse.
kini
6

Si vous utilisez un système Linux, vous pouvez obtenir un nombre aléatoire dans / dev / random ou / dev / urandom. Attention, dev / random bloquera s'il n'y a pas assez de nombres aléatoires disponibles. Si vous avez besoin de vitesse par rapport à l'aléatoire, utilisez / dev / urandom.

Ces "fichiers" seront remplis de nombres aléatoires générés par le système d'exploitation. Cela dépend de l'implémentation de / dev / random sur votre système si vous obtenez des nombres vrais ou pseudo aléatoires. De vrais nombres aléatoires sont générés avec l'aide du bruit provenant des pilotes de périphériques comme la souris, le disque dur, le réseau.

Vous pouvez obtenir des nombres aléatoires à partir du fichier avec dd

Janusz
la source
5

J'ai pris quelques-unes de ces idées et créé une fonction qui devrait fonctionner rapidement si de nombreux nombres aléatoires sont nécessaires.

appeler odcoûte cher si vous avez besoin de beaucoup de numéros aléatoires. Au lieu de cela, je l'appelle une fois et stocke 1024 numéros aléatoires à partir de / dev / urandom. Lorsque randest appelé, le dernier nombre aléatoire est renvoyé et mis à l'échelle. Il est ensuite supprimé du cache. Lorsque le cache est vide, 1024 autres nombres aléatoires sont lus.

Exemple:

rand 10; echo $RET

Renvoie un nombre aléatoire dans RET compris entre 0 et 9 inclus.

declare -ia RANDCACHE
declare -i RET RAWRAND=$(( (1<<32)-1 ))

function rand(){  # pick a random number from 0 to N-1. Max N is 2^32
  local -i N=$1
  [[ ${#RANDCACHE[*]} -eq 0 ]] && { RANDCACHE=( $(od -An -tu4 -N1024 /dev/urandom) ); }  # refill cache
  RET=$(( (RANDCACHE[-1]*N+1)/RAWRAND ))  # pull last random number and scale
  unset RANDCACHE[${#RANDCACHE[*]}-1]     # pop read random number
};

# test by generating a lot of random numbers, then effectively place them in bins and count how many are in each bin.

declare -i c; declare -ia BIN

for (( c=0; c<100000; c++ )); do
  rand 10
  BIN[RET]+=1  # add to bin to check distribution
done

for (( c=0; c<10; c++ )); do
  printf "%d %d\n" $c ${BIN[c]} 
done

MISE À JOUR: Cela ne fonctionne pas si bien pour tous les N. Il gaspille également des bits aléatoires s'il est utilisé avec de petits N. Notant que (dans ce cas) un nombre aléatoire de 32 bits a suffisamment d'entropie pour 9 nombres aléatoires entre 0 et 9 (10 * 9 = 1 000 000 000 <= 2 * 32), nous pouvons extraire plusieurs nombres aléatoires de chaque 32 valeur source aléatoire.

#!/bin/bash

declare -ia RCACHE

declare -i RET             # return value
declare -i ENT=2           # keep track of unused entropy as 2^(entropy)
declare -i RND=RANDOM%ENT  # a store for unused entropy - start with 1 bit

declare -i BYTES=4         # size of unsigned random bytes returned by od
declare -i BITS=8*BYTES    # size of random data returned by od in bits
declare -i CACHE=16        # number of random numbers to cache
declare -i MAX=2**BITS     # quantum of entropy per cached random number
declare -i c

function rand(){  # pick a random number from 0 to 2^BITS-1
  [[ ${#RCACHE[*]} -eq 0 ]] && { RCACHE=( $(od -An -tu$BYTES -N$CACHE /dev/urandom) ); }  # refill cache - could use /dev/random if CACHE is small
  RET=${RCACHE[-1]}              # pull last random number and scale
  unset RCACHE[${#RCACHE[*]}-1]  # pop read random number
};

function randBetween(){
  local -i N=$1
  [[ ENT -lt N ]] && {  # not enough entropy to supply ln(N)/ln(2) bits
    rand; RND=RET       # get more random bits
    ENT=MAX             # reset entropy
  }
  RET=RND%N  # random number to return
  RND=RND/N  # remaining randomness
  ENT=ENT/N  # remaining entropy
};

declare -ia BIN

for (( c=0; c<100000; c++ )); do
  randBetween 10
  BIN[RET]+=1
done

for c in ${BIN[*]}; do
  echo $c
done
philcolbourn
la source
J'ai essayé cela - cela a pris 10 secondes de 100% cpu, puis j'ai imprimé 10 chiffres qui ne semblaient pas aléatoires.
Carlo Wood
Je me souviens maintenant. Ce code génère 100 000 nombres aléatoires. Il place chacun dans un «bac» pour voir à quel point il est aléatoire. Il y a 10 bacs. Ces nombres devraient être similaires si chaque nombre aléatoire entre 0 et 9 est également probable. Si vous souhaitez imprimer chaque numéro, faites écho à $ RET après randBetween 10.
philcolbourn
od -An -tu4 -N40 /dev/urandomgénérera 10 entiers aléatoires non signés de 32 bits séparés par des espaces. vous pouvez le stocker dans un tableau et l'utiliser ensuite. votre code semble exagéré.
Ali
@Ali, OP n'a pas précisé qu'il voulait 32 bits ni aucun autre nombre aléatoire de taille. Moi et d'autres avons interprété cette question comme fournissant un nombre aléatoire dans une plage. Ma fonction rand atteint cet objectif et réduit également la perte d'entropie qui, si elle est épuisée, bloque les programmes. od on / dev / urandom ne renvoie que des nombres aléatoires de 2 ^ N bits et OP devrait alors stocker plusieurs valeurs dans un tableau, les extraire séquentiellement de ce tableau et reconstituer ce tableau. Peut-être pouvez-vous coder cela comme une réponse et gérer d'autres plages de nombres aléatoires?
philcolbourn
@philcolbourn, vous avez raison au sujet de l'OP ne spécifiant pas quel type de nombre aléatoire il veut et cela a manqué mon attention. Mais il a seulement demandé: "Comment générer un nombre aléatoire en bash?". Mon point est qu'il n'a demandé qu'un seul nombre aléatoire. Bien que cette critique s'applique également à mon commentaire précédent (générant 10 nombres aléatoires).
Ali
5

La lecture à partir de fichiers spéciaux de caractères / dev / random ou / dev / urandom est la voie à suivre.

Ces appareils renvoient des nombres vraiment aléatoires lorsqu'ils sont lus et sont conçus pour aider les logiciels d'application à choisir des clés sécurisées pour le chiffrement. Ces nombres aléatoires sont extraits d'un pool d'entropie alimenté par divers événements aléatoires. {LDD3, Jonathan Corbet, Alessandro Rubini et Greg Kroah-Hartman]

Ces deux fichiers sont une interface pour la randomisation du noyau, en particulier

void get_random_bytes_arch(void* buf, int nbytes)

qui tire des octets vraiment aléatoires du matériel si une telle fonction est implémentée par le matériel (généralement), ou il tire du pool d'entropie (composé de temporisations entre les événements comme les interruptions de souris et de clavier et d'autres interruptions enregistrées avec SA_SAMPLE_RANDOM).

dd if=/dev/urandom count=4 bs=1 | od -t d

Cela fonctionne, mais écrit la sortie inutile de ddà stdout. La commande ci-dessous donne juste l'entier dont j'ai besoin. Je peux même obtenir le nombre spécifié de bits aléatoires dont j'ai besoin en ajustant le masque de bits donné à l'expansion arithmétique:

me@mymachine:~/$ x=$(head -c 1 /dev/urandom > tmp && hexdump 
                         -d tmp | head -n 1 | cut -c13-15) && echo $(( 10#$x & 127 ))
4pie0
la source
3

Qu'en est-il de:

perl -e 'print int rand 10, "\n"; '
kh
la source
1
Pour un nombre aléatoire cryptographiquement sécurisé, vous devez lire à partir de / dev / urandom ou utiliser les bibliothèques Crypt :: Random.
kh
3

Je suis peut-être un peu trop tard, mais qu'en est-il de l'utilisation jotpour générer un nombre aléatoire dans une plage dans Bash?

jot -r -p 3 1 0 1

Cela génère un nombre aléatoire ( -r) avec une précision de 3 décimales ( -p). Dans ce cas particulier, vous obtiendrez un nombre entre 0 et 1 ( 1 0 1). Vous pouvez également imprimer des données séquentielles. La source du nombre aléatoire, selon le manuel, est:

Les nombres aléatoires sont obtenus via arc4random (3) lorsqu'aucune graine n'est spécifiée, et via random (3) lorsqu'une graine est donnée.

Vinicius Placco
la source
1
Doit être installé: sudo apt install athena-jot
xerostomus
3

Basé sur les bonnes réponses de @Nelson, @Barun et @Robert, voici un script Bash qui génère des nombres aléatoires.

  • Peut générer le nombre de chiffres souhaité.
  • chaque chiffre est généré séparément, /dev/urandomce qui est bien meilleur que le Bash intégré$RANDOM
#!/usr/bin/env bash

digits=10

rand=$(od -A n -t d -N 2 /dev/urandom |tr -d ' ')
num=$((rand % 10))
while [ ${#num} -lt $digits ]; do
  rand=$(od -A n -t d -N 1 /dev/urandom |tr -d ' ')
  num="${num}$((rand % 10))"
done
echo $num
Euphorie
la source
Exactement ce que je recherchais.
Prometheus
2

Générez un nombre aléatoire dans la plage de 0 à n (entier 16 bits signé). Jeu de résultats dans la variable $ RAND. Par exemple:

#!/bin/bash

random()
{
    local range=${1:-1}

    RAND=`od -t uI -N 4 /dev/urandom | awk '{print $2}'`
    let "RAND=$RAND%($range+1)"
}

n=10
while [ $(( n -=1 )) -ge "0" ]; do
    random 500
    echo "$RAND"
done
Pavlo Bashynskyi
la source
1

Branchement aléatoire d'un programme ou oui / non; 1/0; sortie vrai / faux:

if [ $RANDOM -gt 16383  ]; then              # 16383 = 32767/2 
    echo var=true/1/yes/go_hither
else 
    echo var=false/0/no/go_thither
fi

de si vous paresseux de vous rappeler 16383:

if (( RANDOM % 2 )); then 
    echo "yes"
else 
    echo "no"
fi
xerostomus
la source