Как сгенерировать случайное число в Bash?

236

Как сгенерировать случайное число в диапазоне в Bash?

woakas
источник
21
Насколько случайным это должно быть?
bdonlan

Ответы:

283

Использование $RANDOM. Это часто полезно в сочетании с простой арифметикой оболочки. Например, чтобы сгенерировать случайное число от 1 до 10 (включительно):

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

Фактический генератор в variables.cфункции brand(). Старые версии были простым линейным генератором. Версия 4.0 bashиспользует генератор с цитатой к статье 1985 года, что, вероятно, означает, что это достойный источник псевдослучайных чисел. Я бы не использовал его для симуляции (и уж точно не для криптографии), но, вероятно, он подходит для базовых задач сценариев.

Если вы делаете что-то, что требует серьезных случайных чисел, которые вы можете использовать /dev/randomили /dev/urandomесли они доступны:

$ dd if=/dev/urandom count=4 bs=1 | od -t d
нельсон
источник
21
Будьте осторожны здесь. Несмотря на то, что это нормально, выполнение арифметики со случайными числами может существенно повлиять на случайность вашего результата. в случае $RANDOM % 108 и 9 измеримо (хотя и незначительно) менее вероятно, чем 0-7, даже если $RANDOMэто надежный источник случайных данных.
dimo414
3
@ dimo414 Мне интересно "незначительно", у вас есть источник, где я могу узнать больше об этом?
PascalVKooten
58
Модулируя ваш случайный ввод, вы « запугиваете » результаты. Поскольку $RANDOMдиапазон - 0-32767это числа 0- 7сопоставьте с 3277различными возможными входами, но 8и 9можно получить только 3276разными способами (потому что 32768и 32769не возможно). Это небольшая проблема для быстрых взломов, но означает, что результат не является случайно случайным. Случайные библиотеки, такие как Java Random, предлагают функции для правильного возврата равномерного случайного числа в заданном диапазоне, а не просто для изменения неделимого числа.
dimo414
1
@JFSebastian очень верно - проблема с модулем состоит в том, что он может нарушить однородность любого ГСЧ, не только плохих ГСЧ, но и спасибо за то, что назвали это.
dimo414
14
Просто для контекста, базовый процент голубей для% 10 означает 8 и 9, примерно на 0,03% реже, чем 0–7. Если ваш сценарий оболочки требует более точных равномерных случайных чисел, чем этот, то непременно используйте более сложный и правильный механизм.
Нельсон
71

Пожалуйста, смотрите $RANDOM:

$RANDOM является внутренней функцией Bash (не константой), которая возвращает псевдослучайное целое число в диапазоне от 0 до 32767. Она не должна использоваться для генерации ключа шифрования.

Эндрю Хэйр
источник
1
Имеет ли 32767какое-то особое значение?
Джин Квон
14
@JinKwon 32767- 2^16 / 2 - 1это верхний предел 16-разрядного целого числа со знаком .
Джеффри Мартинес
@JinKwon вы могли бы уточнить, почему вы не говорите, что это так 2^15 - 1? Это эквивалентно, так что мне просто интересно, есть ли какой-то контекст, который я пропускаю?
Бретт Холман
11
@BrettHolman Я думаю, он пытался указать на «подписанную» часть 16-разрядного целого числа со знаком. 2 ^ 16 значений, разделенных пополам для положительных и отрицательных целых.
Коди
Этот ответ не отвечает на вопрос.
отродье
48

Вы также можете использовать shuf (доступно в coreutils).

shuf -i 1-100000 -n 1
knipwim
источник
Как вы проходите в VARS как конец диапазона? Я получил это:shuf -i 1-10 -n 1: syntax error in expression (error token is "1-10 -n 1")
Дата tutbrus
1
Добавьте $varвместо конца диапазона, как это:var=100 && shuf -i 1-${var} -n 1
knipwim
2
Я предпочитаю эту опцию, так как с ней легко генерировать N случайных чисел -n. Например, сгенерировать 5 случайных чисел от 1 до 100 :shuf -i 1-100 -n 5
aerijman
Насколько я понимаю, цифры не случайны. Если вы укажете, shuf -i 1-10 -n 10вы получите все номера от 1 до 10, а точнее один. Если вы укажете, -n 15вы все равно получите только эти 10 номеров ровно один раз. Это действительно только перетасовка, а не генерация случайных чисел.
Радлан
Чтобы получить случайные числа с заменой: -r
Джеффри Андерсон
36

Попробуйте это из вашей оболочки:

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

Здесь -t dуказывает, что формат вывода должен быть десятичным со знаком; -N 1говорит прочитать один байт от /dev/urandom.

Барун
источник
2
Вопрос просит цифры в диапазоне.
JSycamore
9
Вы можете удалить пробелы:od -A n -t d -N 1 /dev/urandom |tr -d ' '
Роберт
23

вы также можете получить случайное число из awk

awk 'BEGIN {
   # seed
   srand()
   for (i=1;i<=1000;i++){
     print int(1 + rand() * 100)
   }
}'
ghostdog74
источник
1
+1 Вы знаете, сначала я подумал, зачем вам это делать, но на самом деле мне это очень нравится.
Zelanix
1
Спасибо за предоставление решения, которое включает посев. Я не мог найти это нигде!
so.very.tired
2
+1 за посев. Возможно, стоит упомянуть, что srand()семя - это текущее время процессора. Если вам нужно указать конкретное начальное число, чтобы RNG можно было продублировать, используйте srand(x)где xначальное число. Кроме того, согласно цитате из руководства по числовым функциям GNU awk, «в различных реализациях awk используются разные генераторы случайных чисел». В результате, если вы заинтересованы в создании статистического распределения, следует ожидать небольших изменений при переходе от одной среды выполнения к другой на другой платформе (все запущены awkили gawk).
Cbhihe
18

Есть $ RANDOM. Я не знаю точно, как это работает. Но это работает. Для тестирования вы можете сделать:

echo $RANDOM
Антуан Клавал
источник
16

Мне нравится этот трюк:

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

...

fraff
источник
7
$ RANDOM находится в диапазоне от 0 до 32767. В итоге вы получите больше чисел, начинающихся с 1, 2 или 3, чем 4-9. Если у вас все в порядке с несбалансированным дистрибутивом, это будет нормально работать.
jbo5112
2
@ jbo5112 Вы совершенно правы, а как насчет отображения последней цифры? echo $ {RANDOM: 0-1} для одной цифры, $ {RANDOM: 0-2} для одной цифры ...?
Фрафф
4
Если вы используете последнюю цифру (ы), это будет довольно хорошо, но она будет включать 0 и 00. На одной цифре 0-7 будет встречаться на 0,03% чаще, чем 8-9. На 2 цифрах 0-67 будет встречаться на 0,3% чаще, чем 68-99. Если вам нужно хорошее распределение случайных чисел, надеюсь, вы не используете bash. С оригиналом: ${RANDOM:0:1}имеет шанс 67,8% дать вам 1 или 2, ${RANDOM:0:2}имеет шанс только 0,03% дать вам однозначное число (должно быть 1%), и оба имеют 0,003% шанс дать вам 0 . Есть все еще случаи использования, где это хорошо (например, непоследовательный ввод).
jbo5112
12

Случайное число от 0 до 9 включительно.

echo $((RANDOM%10))
Дэвид Ньюкомб
источник
2
Мой плохой, не прочитал справочную страницу должным образом. $RANDOMтолько идет от 0 до 32767. Он должен был сказать «Случайное число в основном от 1 до 3, с несколькими вингерами»;)
Дэвид Ньюкомб
1
Какой? Это все равно будет между 0 и 9, хотя вероятность появления 8 и 9 будет немного меньше, чем 0-7, как упоминалось в другом ответе.
Кино
6

Если вы используете систему Linux, вы можете получить случайное число из / dev / random или / dev / urandom. Будьте осторожны / dev / random будет блокировать, если недостаточно доступных случайных чисел. Если вам нужна скорость по случайности, используйте / dev / urandom.

Эти «файлы» будут заполнены случайными числами, сгенерированными операционной системой. Это зависит от реализации / dev / random в вашей системе, если вы получите истинные или псевдослучайные числа. Истинные случайные числа генерируются с помощью шума, получаемого от драйверов устройств, таких как мышь, жесткий диск, сеть

Вы можете получить случайные числа из файла с дд

Януш
источник
5

Я взял несколько из этих идей и создал функцию, которая должна быстро работать, если требуется много случайных чисел.

звонить odдорого, если вам нужно много случайных чисел. Вместо этого я вызываю его один раз и сохраняю 1024 случайных числа из / dev / urandom. Когда randвызывается, последнее случайное число возвращается и масштабируется. Затем он удаляется из кэша. Когда кеш пуст, читаются еще 1024 случайных числа.

Пример:

rand 10; echo $RET

Возвращает случайное число в RET от 0 до 9 включительно.

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

ОБНОВЛЕНИЕ: Это не работает так хорошо для всех N. Это также тратит впустую случайные биты, если используется с маленьким N. Отметив, что (в этом случае) 32-битное случайное число имеет достаточно энтропии для 9 случайных чисел от 0 до 9 (10 * 9 = 1 000 000 000 <= 2 * 32) мы можем извлечь несколько случайных чисел из каждого 32 случайного значения источника.

#!/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
источник
Я попробовал это - потребовалось 10 секунд 100% процессора, а затем напечатал 10 чисел, которые не выглядели случайными для ВСЕХ.
Карло Вуд
Я вспомнил. Этот код генерирует 100 000 случайных чисел. Он помещает каждого в «мусорное ведро», чтобы посмотреть, насколько оно случайное. Есть 10 бункеров. Эти числа должны быть одинаковыми, если каждое случайное число от 0 до 9 одинаково вероятно. Если вы хотите напечатать каждое число, выведите $ RET после randBetween 10.
philcolbourn
od -An -tu4 -N40 /dev/urandomсгенерирует 10 случайных беззнаковых 32-битных целых чисел, разделенных пробелом. Вы можете сохранить его в массиве и использовать позже. ваш код кажется излишним.
Али
@ Али, ОП не указали, что им нужно 32-битное или случайное число другого размера. Я и некоторые другие интерпретировали этот вопрос как предоставление случайного числа в пределах диапазона. Моя функция rand достигает этой цели, а также уменьшает потерю энтропии, которая, если она исчерпана, приводит к блокировке программ. od on / dev / urandom возвращает только 2 ^ N битных случайных чисел, и тогда OP потребуется сохранить несколько значений в массиве, последовательно извлекая их из этого массива и пополняя этот массив. Возможно, вы можете закодировать это как ответ и обработать другие диапазоны случайных чисел?
Филколбурн
@philcolbourn, вы правы в том, что ОП не указывает, какое случайное число он хочет, и оно пропустило мое внимание. Но он только спросил: «Как создать в случайное число в Баш?». Я хочу сказать, что он попросил только одно случайное число. Хотя этот критик относится и к моему предыдущему комментарию (генерирует 10 случайных чисел), а также.
Али
5

Чтение из специальных файлов символов / dev / random или / dev / urandom - это лучший способ.

Эти устройства возвращают действительно случайные числа при чтении и разработаны, чтобы помочь прикладному программному обеспечению выбрать безопасные ключи для шифрования. Такие случайные числа извлекаются из пула энтропии, который вносится различными случайными событиями. {LDD3, Джонатан Корбет, Алессандро Рубини и Грег Кроа-Хартман]

Эти два файла являются интерфейсом для рандомизации ядра, в частности

void get_random_bytes_arch(void* buf, int nbytes)

который извлекает действительно случайные байты из аппаратного обеспечения, если такая функция реализована аппаратным обеспечением (обычно таковым является), или извлекает из пула энтропии (состоящего из временных интервалов между событиями, такими как прерывания мыши и клавиатуры и другие прерывания, которые зарегистрированы в SA_SAMPLE_RANDOM).

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

Это работает, но записывает ненужные выходные данные ddв stdout. Команда ниже дает только целое число, которое мне нужно. Я даже могу получить указанное количество случайных битов, сколько мне нужно, путем настройки битовой маски, данной арифметическому расширению:

me@mymachine:~/$ x=$(head -c 1 /dev/urandom > tmp && hexdump 
                         -d tmp | head -n 1 | cut -c13-15) && echo $(( 10#$x & 127 ))
4pie0
источник
3

Что о:

perl -e 'print int rand 10, "\n"; '
кк
источник
1
Для криптографически безопасного случайного числа вам нужно прочитать из / dev / urandom или использовать библиотеки Crypt :: Random.
кк
3

Может быть, я немного опоздал, но как насчет jotгенерации случайного числа в диапазоне в Bash?

jot -r -p 3 1 0 1

Это генерирует случайное ( -r) число с точностью до 3 десятичных знаков ( -p). В этом конкретном случае вы получите одно число от 0 до 1 ( 1 0 1). Вы также можете распечатать последовательные данные. Источник случайного числа, согласно инструкции, является:

Случайные числа получаются через arc4random (3), когда начальное число не указано, и через random (3), когда задано начальное число.

Виниций Плакко
источник
1
Должно быть установлено: sudo apt install athena-jot
xerostomus
3

Основываясь на великолепных ответах @Nelson, @Barun и @Robert, вот скрипт Bash, который генерирует случайные числа.

  • Может генерировать, сколько цифр вы хотите.
  • каждая цифра генерируется отдельно, /dev/urandomчто намного лучше, чем встроенный в 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
Эйфория
источник
Именно то, что я был после.
Прометей
2

Генерация случайного числа в диапазоне от 0 до n (16-разрядное целое число со знаком). Результат устанавливается в переменной $ RAND. Например:

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

Случайная ветвление программы или да / нет; 1/0; истина / ложь вывод:

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

если вам лень вспоминать 16383:

if (( RANDOM % 2 )); then 
    echo "yes"
else 
    echo "no"
fi
xerostomus
источник