Como gerar um número aleatório no Bash?


236

Como gerar um número aleatório dentro de um intervalo no Bash?


21
Quão aleatório ele precisa ser?
bdonlan

Respostas:


283

Use $RANDOM. Geralmente é útil em combinação com a aritmética simples do shell. Por exemplo, para gerar um número aleatório entre 1 e 10 (inclusive):

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

O gerador atual está dentro variables.c, a função brand(). As versões mais antigas eram um gerador linear simples. A versão 4.0 de bashusa um gerador com uma citação de um artigo de 1985, o que presumivelmente significa que é uma fonte decente de números pseudo-aleatórios. Eu não o usaria para uma simulação (e certamente não para criptografia), mas provavelmente é adequado para tarefas básicas de script.

Se você estiver fazendo algo que exija números aleatórios sérios, poderá usar /dev/randomou /dev/urandomse estiverem disponíveis:

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

21
Tenha cuidado aqui. Enquanto isso é bom em uma pitada, fazer aritmética em números aleatórios pode afetar drasticamente a aleatoriedade do seu resultado. no caso de $RANDOM % 10, 8 e 9 são mensuráveis ​​(embora marginalmente) menos prováveis ​​que 0-7, mesmo que $RANDOMseja uma fonte robusta de dados aleatórios.
Dimo414

3
@ dimo414 Estou curioso para "marginalmente", você tem uma fonte onde eu possa descobrir mais sobre isso?
PascalVKooten

58
Ao modular sua entrada aleatória, você está " furando " os resultados. Como $RANDOMo intervalo é 0-32767o número 0- 7mapeie para 3277diferentes entradas possíveis, mas 8e 9só pode ser produzido de 3276maneiras diferentes (porque 32768e 32769não são possíveis). Esse é um problema menor para hacks rápidos, mas significa que o resultado não é uniformemente aleatório. Bibliotecas aleatórias, como as de Java Random, oferecem funções para retornar adequadamente um número aleatório uniforme no intervalo especificado, em vez de simplesmente modificar um número não divisível.
Dimo414 26/05

1
@JFSebastian é verdade - o problema com o módulo é que ele pode quebrar a uniformidade de qualquer RNG, não apenas PRNGs ruins, mas obrigado por chamar isso.
precisa saber é o seguinte

14
Apenas por contexto, o escaneamento básico para% 10 significa 8 e 9 são cerca de 0,03% menos prováveis ​​de ocorrer do que 0–7. Se o seu script shell requer números aleatórios uniformes mais precisos do que isso, use todos os meios de um mecanismo mais complexo e adequado.
Nelson Nelson

71

Por favor veja $RANDOM:

$RANDOM é uma função Bash interna (não uma constante) que retorna um número inteiro pseudoaleatório no intervalo de 0 a 32767. Não deve ser usada para gerar uma chave de criptografia.


1
Será que 32767tem algum significado especial?
Jin Kwon

14
@JinKwon 32767é 2^16 / 2 - 1o limite superior para um número inteiro assinado de 16 bits.
10137 Jeffrey Martinez

@JinKwon, você poderia esclarecer por que não diz que é 2^15 - 1? É equivalente, então estou curioso para saber se há algum contexto em falta?
Brett Holman

11
@BrettHolman Acho que ele estava tentando apontar a parte "assinada" do inteiro assinado de 16 bits. 2 ^ 16 valores, divididos ao meio para entradas positivas e negativas.
Cody

Esta resposta não responde à pergunta.
brat

48

Você também pode usar shuf (disponível em coreutils).

shuf -i 1-100000 -n 1

Como você passa em Vars no final do intervalo? Eu consegui:shuf -i 1-10 -n 1: syntax error in expression (error token is "1-10 -n 1")
dat tutbrus

1
Adicionar um $varao invés do fim gama, como este:var=100 && shuf -i 1-${var} -n 1
knipwim

2
Prefiro essa opção, pois é fácil gerar N números aleatórios -n. Por exemplo, gere 5 números aleatórios entre 1 e 100 :shuf -i 1-100 -n 5
aerijman 14/01

Tanto quanto eu entendo os números não são aleatórios. Se você especificar shuf -i 1-10 -n 10, obterá todos os números de 1 a 10, exatamente um. Se você especificar -n 15, ainda receberá apenas esses 10 números exatamente uma vez. Isso é realmente apenas baralhar, não gerar números aleatórios.
radlan

Para obter números aleatórios com a substituição: -r
Geoffrey Anderson

36

Tente isso no seu shell:

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

Aqui, -t despecifica que o formato de saída deve ser assinado como decimal; -N 1diz para ler um byte de /dev/urandom.


2
A pergunta pede números em um intervalo.
JSycamore 17/05

9
você pode remover espaços:od -A n -t d -N 1 /dev/urandom |tr -d ' '
Robert

23

você também pode obter um número aleatório do awk

awk 'BEGIN {
   # seed
   srand()
   for (i=1;i<=1000;i++){
     print int(1 + rand() * 100)
   }
}'

1
+1 você sabe, no começo eu pensei por que você iria querer fazer assim, mas na verdade eu gosto bastante.
zelanix

1
Obrigado por fornecer uma solução que inclui a propagação. Não encontrei em lugar algum!
so.very.tired

2
+1 para semear. Vale a pena mencionar que srand()a semente é o tempo atual da CPU. Se você precisar especificar uma semente específica, para que o RNG possa ser duplicado, use srand(x)onde xestá a semente. Além disso, citado no manual de funções numéricas do GNU awk, "diferentes implementações do awk usam diferentes geradores de números aleatórios internamente". O resultado é que, se você estiver interessado em gerar distribuição estatística, espere pequenas variações passando de um tempo de execução para outro em uma plataforma diferente (todas em execução awkou gawk).
24419 Cbhihe

18

Há $ ALEATÓRIO. Não sei exatamente como isso funciona. Mas funciona. Para testar, você pode fazer:

echo $RANDOM

16

Eu gosto deste truque:

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

...


7
$ RANDOM está no intervalo de 0 a 32767. Você terminará com mais números que começam com 1, 2 ou 3 do que com 4-9. Se você estiver de acordo com uma distribuição desequilibrada, isso funcionará bem.
precisa saber é o seguinte

2
@ jbo5112 você está totalmente certo, que tal exibir o último dígito? eco $ {RANDOM: 0-1} para um dígito, $ {RANDOM: 0-2} para dois dígitos ...?
fraff

4
Se você usar o (s) último (s) dígito (s), ele será bastante bom, mas incluirá zeros e zeros. Em um único dígito, 0-7 ocorrerá 0,03% com mais frequência que 8-9. Em 2 dígitos, 0-67 ocorrerá 0,3% mais frequentemente que 68-99. Se você precisar de uma distribuição de números aleatórios tão boa, espero que você não esteja usando o bash. Com o original: ${RANDOM:0:1}tem 67,8% de chance de fornecer 1 ou 2, ${RANDOM:0:2}apenas 0,03% de chance de fornecer um número de dígito único (deve ser 1%) e ambos têm 0,003% de chance de fornecer 0 Ainda existem casos de uso em que isso é bom (por exemplo, entrada não consistente).
precisa saber é o seguinte

12

Número aleatório entre 0 e 9, inclusive.

echo $((RANDOM%10))

2
Meu mal, não leu a página de manual corretamente. $RANDOMsó vai de 0 a 32767. Deveria ter dito "Número aleatório principalmente entre 1 e 3, com alguns alas";)
David Newcomb

1
O que? Ainda estará entre 0 e 9, embora 8 e 9 tenham uma probabilidade ligeiramente menor de ocorrer que 0 a 7, conforme mencionado em outra resposta.
Kini #

6

Se você estiver usando um sistema Linux, poderá obter um número aleatório de / dev / random ou / dev / urandom. Tenha cuidado / dev / random irá bloquear se não houver números aleatórios suficientes disponíveis. Se você precisar de velocidade acima da aleatoriedade, use / dev / urandom.

Esses "arquivos" serão preenchidos com números aleatórios gerados pelo sistema operacional. Depende da implementação de / dev / random no seu sistema se você obtiver números verdadeiros ou pseudo-aleatórios. Números aleatórios verdadeiros são gerados com a ajuda do ruído coletado de drivers de dispositivos como mouse, disco rígido e rede.

Você pode obter números aleatórios do arquivo com dd


5

Eu peguei algumas dessas idéias e criei uma função que deveria ser executada rapidamente se forem necessários muitos números aleatórios.

ligar odé caro se você precisar de muitos números aleatórios. Em vez disso, chamo uma vez e armazeno 1024 números aleatórios em / dev / urandom. Quandorand é chamado, o último número aleatório é retornado e redimensionado. É então removido do cache. Quando o cache está vazio, outros 1024 números aleatórios são lidos.

Exemplo:

rand 10; echo $RET

Retorna um número aleatório no RET entre 0 e 9, inclusive.

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

UPDATE: Isso não funciona tão bem para todos os N. Ele também desperdiça bits aleatórios se usado com N. pequeno. Observando que (nesse caso) um número aleatório de 32 bits tem entropia suficiente para 9 números aleatórios entre 0 e 9 (10 * 9 = 1.000.000.000 <= 2 * 32), podemos extrair vários números aleatórios de cada 32 valor fonte aleatório.

#!/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

Eu tentei isso - foram necessários 10 segundos de 100% da CPU e, em seguida, foram impressos 10 números que não pareciam aleatórios.
Carlo Wood

Eu me lembro agora. Este código gera 100.000 números aleatórios. Ele coloca cada um em um 'bin' para ver como é aleatório. Existem 10 caixas. Esses números devem ser semelhantes se cada número aleatório entre 0 e 9 for igualmente provável. Se você deseja imprimir cada número, echo $ RET após RANDBETWEEN 10.
philcolbourn

od -An -tu4 -N40 /dev/urandomirá gerar 10 números inteiros aleatórios e não assinados, separados por espaços em branco. você pode armazená-lo em uma matriz e usá-lo posteriormente. seu código parece ser um exagero.
Ali

@ Ali, OP não especificou que eles queriam 32 bits nem qualquer outro número aleatório de tamanho. Eu e alguns outros interpretamos essa pergunta como fornecendo um número aleatório dentro de um intervalo. Minha função rand atinge esse objetivo e também reduz a perda de entropia que, se esgotada, causa o bloqueio de programas. od on / dev / urandom retorna apenas números aleatórios de 2 ^ N bits e o OP precisaria armazenar vários valores em uma matriz, extraindo-os sequencialmente dessa matriz e reabastecendo essa matriz. Talvez você possa codificar isso como resposta e lidar com outros intervalos de números aleatórios?
#

@philcolbourn, você está certo sobre o OP não especificar que tipo de número aleatório ele quer e perdeu minha atenção. Mas ele apenas perguntou: "Como gerar um número aleatório no bash?". O que quero dizer é que ele só pediu um número aleatório. Embora esse crítico se aplique ao meu comentário anterior (gerando 10 números aleatórios) também.
Ali

5

Ler arquivos especiais de caracteres / dev / random ou / dev / urandom é o caminho a percorrer.

Esses dispositivos retornam números verdadeiramente aleatórios quando lidos e foram projetados para ajudar o software aplicativo a escolher chaves seguras para criptografia. Esses números aleatórios são extraídos de um pool de entropia que é contribuído por vários eventos aleatórios. {LDD3, Jonathan Corbet, Alessandro Rubini e Greg Kroah-Hartman]

Esses dois arquivos são uma interface para a randomização do kernel, em particular

void get_random_bytes_arch(void* buf, int nbytes)

que extrai bytes verdadeiramente aleatórios do hardware, se essa função é implementada pelo hardware (geralmente é), ou extrai do pool de entropia (composto por tempos entre eventos como interrupções de mouse e teclado e outras interrupções registradas no SA_SAMPLE_RANDOM).

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

Isso funciona, mas grava saída desnecessária de ddpara stdout. O comando abaixo fornece apenas o número inteiro que eu preciso. Posso até obter um número especificado de bits aleatórios, conforme necessário, ajustando a máscara de bits dada à expansão aritmética:

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

3

A respeito:

perl -e 'print int rand 10, "\n"; '

1
Para um número aleatório criptograficamente seguro, você precisa ler em / dev / urandom ou usar as bibliotecas Crypt :: Random.
kh

3

Talvez eu esteja um pouco atrasado, mas e quanto a usar jotpara gerar um número aleatório dentro de um intervalo no Bash?

jot -r -p 3 1 0 1

Isso gera um -rnúmero aleatório ( ) com precisão de 3 casas decimais ( -p). Nesse caso específico, você obterá um número entre 0 e 1 ( 1 0 1). Você também pode imprimir dados seqüenciais. A fonte do número aleatório, de acordo com o manual, é:

Os números aleatórios são obtidos através do arc4random (3) quando nenhuma semente é especificada e através do aleatório (3) quando uma semente é fornecida.


1
Deve ser instalado: sudo apt install athena-jot
xerostomus

3

Com base nas ótimas respostas de @Nelson, @Barun e @Robert, aqui está um script Bash que gera números aleatórios.

  • Pode gerar quantos dígitos você deseja.
  • cada dígito é gerado separadamente, o /dev/urandomque é muito melhor do que o built-in do Bash$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

Exatamente o que eu estava procurando.
Prometheus

2

Gere um número aleatório no intervalo de 0 a n (número inteiro assinado de 16 bits). Conjunto de resultados na variável $ RAND. Por exemplo:

#!/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

1

Ramificação aleatória de um programa ou sim / não; 1/0; saída verdadeira / falsa:

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 se você tem preguiça de lembrar 16383:

if (( RANDOM % 2 )); then 
    echo "yes"
else 
    echo "no"
fi
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.