如何在 Bash 中生成随机数?
Posted
技术标签:
【中文标题】如何在 Bash 中生成随机数?【英文标题】:How to generate random number in Bash? 【发布时间】:2010-11-14 18:11:46 【问题描述】:如何在 Bash 中生成一个范围内的随机数?
【问题讨论】:
它需要有多随机? 【参考方案1】:使用$RANDOM
。它通常与简单的 shell 算术结合使用。例如,要生成 1 到 10(含)之间的随机数:
$ echo $((1 + $RANDOM % 10))
3
实际的生成器在variables.c
,函数brand()
。 Older versions 是一个简单的线性生成器。 bash
的 4.0 版在 1985 年的一篇论文中使用了 generator with a citation,这可能意味着它是一个不错的伪随机数来源。我不会将它用于模拟(当然也不会用于加密),但它可能足以完成基本的脚本任务。
如果您正在做一些需要大量随机数的事情,您可以使用/dev/random
或/dev/urandom
(如果可用):
$ dd if=/dev/urandom count=4 bs=1 | od -t d
【讨论】:
这里要小心。虽然这在紧要关头很好,但对随机数进行算术运算会极大地影响结果的随机性。在$RANDOM % 10
的情况下,即使$RANDOM
是随机数据的可靠来源,8 和 9 的概率也明显低于 0-7。
@dimo414 我对“勉强”感到好奇,你有什么来源可以让我找到更多相关信息吗?
通过对你的随机输入取模,你就是“pigeon-holing”的结果。由于$RANDOM
的范围是0-32767
数字0
-7
映射到3277
不同的可能输入,但8
和9
只能以不同的方式产生3276
(因为@987654343 @ 和 32769
是不可能的)。这对于快速破解来说是一个小问题,但意味着结果不是完全随机的。随机库,如 Java 的 Random
,提供了在给定范围内正确返回统一随机数的函数,而不是简单地修改不可整除的数字。
@JFSebastian 非常正确 - 模数的问题是它可以打破 任何 RNG 的一致性,不仅仅是糟糕的 PRNG,但感谢您提出这一点。
就上下文而言,% 10 的基本分类意味着 8 和 9 发生的可能性比 0-7 低约 0.03%。如果你的 shell 脚本需要比这更准确的统一随机数,那么一定要使用更复杂和更合适的机制。【参考方案2】:
请看$RANDOM
:
$RANDOM
是一个内部 Bash 函数 (不是常数)返回一个 范围内的伪随机整数 0 - 32767。它不应用于 生成一个加密密钥。
【讨论】:
32767
有什么特殊含义吗?
@JinKwon 32767
是 2^16 / 2 - 1
,这是有符号 16 位整数的上限。
@JinKwon 你能澄清一下你为什么不说它是2^15 - 1
吗?它是等价的,所以我只是想知道我是否缺少某些上下文?
@BrettHolman 我认为他试图指出有符号 16 位整数的“有符号”部分。 2^16 个值,正负整数分成两半。
当我使用 sh
而不是 bash
运行我的脚本时,这总是为我返回 0。这是为什么呢?【参考方案3】:
您也可以使用shuf
(在 coreutils 中可用)。
shuf -i 1-100000 -n 1
【讨论】:
如何将变量作为范围结束传递?我得到了这个:shuf -i 1-10 -n 1: syntax error in expression (error token is "1-10 -n 1")
添加一个$var
而不是范围结束,像这样:var=100 && shuf -i 1-$var -n 1
我更喜欢这个选项,因为它很容易用-n
生成 N 个随机数。例如。 生成 5 个 1 到 100 之间的随机数:shuf -i 1-100 -n 5
据我了解,这些数字不是随机的。如果您指定shuf -i 1-10 -n 10
,您将获得从 1 到 10 的所有数字。如果您指定-n 15
,您仍将仅获得这 10 个数字一次。这实际上只是洗牌,而不是生成随机数。
获取带替换的随机数:-r【参考方案4】:
在你的 shell 中试试这个:
$ od -A n -t d -N 1 /dev/urandom
这里,-t d
指定输出格式应该是有符号十进制; -N 1
表示从 /dev/urandom
读取一个字节。
【讨论】:
该问题要求输入范围内的数字。 可以去掉空格:od -A n -t d -N 1 /dev/urandom |tr -d ' '
【参考方案5】:
你也可以从 awk 中获取随机数
awk 'BEGIN
# seed
srand()
for (i=1;i<=1000;i++)
print int(1 + rand() * 100)
'
【讨论】:
+1 你知道的,起初我虽然你为什么要这样做,但实际上我很喜欢。 感谢您提供包含播种的解决方案。我在任何地方都找不到! +1 用于播种。值得一提的是srand()
的种子是当前的 CPU 时间。如果您需要指定特定的种子,以便可以复制 RNG,请使用srand(x)
,其中x
是种子。此外,引用 GNU awk 的数字函数手册,“不同的 awk 实现在内部使用不同的随机数生成器。”结果是,如果您对生成统计分布感兴趣,您应该期望在不同平台上从一个运行时到下一个运行时会有细微的变化(全部运行 awk
或 gawk
)。【参考方案6】:
有 $RANDOM。 我不知道它是如何工作的。但它有效。 对于测试,您可以这样做:
echo $RANDOM
【讨论】:
我们如何获得1-10范围内的随机数【参考方案7】:我喜欢这个技巧:
echo $RANDOM:0:1 # random number between 1 and 9
echo $RANDOM:0:2 # random number between 1 and 99
...
【讨论】:
$RANDOM 在 0 - 32767 的范围内。以 1、2 或 3 开头的数字将比 4-9 多。如果你对不平衡分布没问题,这会很好。 @jbo5112 你完全正确,那么显示最后一位呢? echo $RANDOM:0-1 代表一位,$RANDOM:0-2 代表两位... ? 如果使用最后一个数字,会比较好,但它会包含 0 和 00。在单个数字上,0-7 的出现频率比 8-9 高 0.03%。在 2 位数字上,0-67 的出现频率比 68-99 高 0.3%。如果您需要一个很好的随机数分布,希望您没有使用 bash。原文:$RANDOM:0:1
有 67.8% 的机会给你 1 或 2,$RANDOM:0:2
只有 0.03% 的机会给你一个数字(应该是 1%),两者都有 0.003%给你一个 0 的机会。仍有一些用例可以做到这一点(例如不一致的输入)。【参考方案8】:
0 到 9 之间的随机数。
echo $((RANDOM%10))
【讨论】:
我的错,没有正确阅读手册页。$RANDOM
只从 0 到 32767。它应该说“随机数大多在 1 到 3 之间,有几个边锋”;)
什么?它仍然在 0 到 9 之间,尽管 8 和 9 发生的概率比 0 到 7 略低,如另一个答案中所述。【参考方案9】:
bash 5.1 引入了一个新变量SRANDOM
,它从系统的熵引擎中获取其随机数据,因此不是线性的,并且不能重新播种以获得相同的随机序列。此变量可用作RANDOM
的替代品,用于生成更多随机数。
$ echo $((1 + SRANDOM % 10))
4
【讨论】:
在第四次尝试时为我生成了一个不同的数字。谢谢! 作为一个 32 位数字,它最多可达 2 147 483 647。【参考方案10】:如果您使用的是 linux 系统,您可以从/dev/random or /dev/urandom 中获取随机数。如果没有足够的随机数可用,请注意 /dev/random 将阻塞。如果您需要速度而不是随机性,请使用 /dev/urandom。
这些“文件”将被操作系统生成的随机数填充。如果您获得真或伪随机数,这取决于您系统上 /dev/random 的实现。真正的随机数是在从鼠标、硬盘驱动器、网络等设备驱动程序收集的噪声帮助下生成的。
您可以使用dd从文件中获取随机数
【讨论】:
【参考方案11】:从 /dev/random 或 /dev/urandom 字符特殊文件读取是要走的路。
这些设备在读取和设计时会返回真正的随机数 帮助应用软件选择安全密钥进行加密。这样的 从贡献的熵池中提取随机数 通过各种随机事件。 LDD3,乔纳森·科贝特,亚历山德罗 Rubini 和 Greg Kroah-Hartman]
这两个文件是内核随机化的接口,特别是
void get_random_bytes_arch(void* buf, int nbytes)
如果这样的功能是由硬件实现的(通常是),它会从硬件中提取真正的随机字节,或者它从熵池中提取(包括鼠标和键盘中断等事件之间的时序以及在 SA_SAMPLE_RANDOM 中注册的其他中断)。
dd if=/dev/urandom count=4 bs=1 | od -t d
这可行,但会将不需要的输出从dd
写入标准输出。下面的命令只给出了我需要的整数。我什至可以通过调整给算术扩展的位掩码来获得我需要的指定数量的随机位:
me@mymachine:~/$ x=$(head -c 1 /dev/urandom > tmp && hexdump
-d tmp | head -n 1 | cut -c13-15) && echo $(( 10#$x & 127 ))
【讨论】:
【参考方案12】:我采用了其中的一些想法,并制作了一个在需要大量随机数时应该快速执行的函数。
如果您需要大量随机数,调用 od
会很昂贵。相反,我调用它一次并从 /dev/urandom 存储 1024 个随机数。当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 位随机数对于 0 到 9 之间的 9 个随机数具有足够的熵( 10*9=1,000,000,000 *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
【讨论】:
我试过了 - 100% cpu 用了 10 秒,然后打印了 10 个看起来一点也不随机的数字。 我现在记得了。此代码生成 100,000 个随机数。它将每个都放在一个“bin”中以查看它的随机性。有 10 个垃圾箱。如果 0 到 9 之间的每个随机数的可能性相同,则这些数字应该相似。如果要打印每个数字,请在 randBetween 10 之后回显 $RET。od -An -tu4 -N40 /dev/urandom
将生成 10 个用空格分隔的随机无符号 32 位整数。您可以将其存储在一个数组中,然后再使用它。您的代码似乎有点矫枉过正。
@Ali,OP 没有指定他们想要 32 位或任何其他大小的随机数。我和其他一些人将此问题解释为提供一个范围内的随机数。我的 rand 函数实现了这个目标,还减少了熵的损失,如果用尽,会导致程序阻塞。 /dev/urandom 上的 od 仅返回 2^N 位随机数,然后 OP 需要将多个值存储到一个数组中,依次从该数组中提取它们,并补充该数组。也许您可以将此编码为答案并处理其他随机数范围?
@philcolbourn,您对 OP 没有指定他想要什么样的随机数是正确的,它错过了我的注意。但他只问:“如何在 bash 中生成 a 随机数?”。我的意思是他只要求一个随机数。尽管这个批评者也适用于我之前的评论(生成 10 个随机数)。【参考方案13】:
生成 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
【讨论】:
【参考方案14】:怎么样:
perl -e 'print int rand 10, "\n"; '
【讨论】:
对于加密安全的随机数,您需要从 /dev/urandom 读取或使用 Crypt::Random 库。【参考方案15】:也许我有点晚了,但是在 Bash 中使用jot
生成一个范围内的随机数怎么样?
jot -r -p 3 1 0 1
这会生成一个随机数 (-r
),精度为 3 位小数 (-p
)。在这种特殊情况下,您将获得一个介于 0 和 1 之间的数字 (1 0 1
)。您还可以打印顺序数据。根据手册,随机数的来源是:
没有指定种子时,通过arc4random(3)获取随机数,通过 随机(3)当一个种子给出。
【讨论】:
必须安装:sudo apt install athena-jot 在 macOS 10.14 上开箱即用。【参考方案16】:我为此写了几篇文章。
https://linuxconfig.org/generating-random-numbers-in-bash-with-examples https://linuxconfig.org/random-entropy-in-bash https://www.cloudsavvyit.com/7572/how-to-generate-better-random-numbers-at-the-bash-command-line/$ RANDOM=$(date +%s%N | cut -b10-19)
$ echo $(( $RANDOM % 113 + 13 ))
上面将给出一个介于 13 和 125 (113-1+13) 之间的数字,具有合理的随机熵。
【讨论】:
我添加了一个调整来去掉前导零: RANDOM=$(date +%s%N | cut -b10-19 | sed -e 's/^0*//;s/ ^$/0/') @JCCyC 有趣的想法!为什么要删除空白并与0
交换呢?即那不应该发生在开始?
它发生在我身上,来自date +%s%N
的最后 9 位数字中的第一个有时为零,我输入数字的代码以为它是八进制的。 :-O 第二个 s 命令用于最后 9 位全为零的罕见情况,以保证输出不会为空,而是单个“0”。
事实上,它每 10 秒发生一秒——每当 Epoch 秒的整数以 0 结尾时。试试这个有趣:while [ 1 ];做回声 -en date +"%s %N"
" "date +%s%N | cut -b10-19
"\r";完成
呃,反引号把事情搞砸了。让我再试一次。 while [ 1 ]; do echo -en $(date +"%s %N")" "$(date +%s%N | cut -b10-19)"\r"; done
【参考方案17】:
根据@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
【讨论】:
【参考方案18】:程序的随机分支或是/否; 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
【讨论】:
【参考方案19】:生成随机 3 位数字
这对于创建示例数据非常有用。示例:将所有测试数据放在名为“test-create-volume-123”的目录中,然后在测试完成后,对整个目录进行 zap。通过精确生成三位数字,您就没有weird sorting issues。
printf '%02d\n' $((1 + RANDOM % 100))
这会缩小,例如一位数:
printf '%01d\n' $((1 + RANDOM % 10))
它可以放大,但只有四位数。原因见上文:)
【讨论】:
【参考方案20】:想要在没有 dd 和 od
的情况下使用 /dev/urandomfunction roll() local modulus=$1:-6; echo $(( 1 + 0x$(env LC_CTYPE=C tr -dc '0-9a-fA-F' < /dev/urandom | head -c5 ) % $modulus ));
测试
$ roll
5
$ roll 12
12
它有多随机?
$ (echo "count roll percentage"; i=0; while [ $i -lt 10000 ]; do roll; i=$((i+1)); done | sort | uniq -c | awk 'print $0,($1/10000*100)"%"') | column -t
count roll percentage
1625 1 16.25%
1665 2 16.65%
1646 3 16.46%
1720 4 17.2%
1694 5 16.94%
1650 6 16.5%
【讨论】:
【参考方案21】:使用 perl 生成 n 位随机数的 bash 函数。指定位数或 n 0 的模板。
rand()
perl -E '$ARGV[0]||=""; $ARGV[0]=int($ARGV[0])||length($ARGV[0]); say join "", int(rand(9)+1)*($ARGV[0]?1:0), map int(rand(10)) (0..($ARGV[0]||0)-2)' $1
用法:
$ rand 3
381
$ rand 000
728
调用rand n的演示,对于0到15之间的n:
$ for n in 0..15; do printf "%02d: %s\n" $n $(rand $n); done
00: 0
01: 3
02: 98
03: 139
04: 1712
05: 49296
06: 426697
07: 2431421
08: 82727795
09: 445682186
10: 6368501779
11: 51029574113
12: 602518591108
13: 5839716875073
14: 87572173490132
15: 546889624135868
调用rand n的演示,对于n是一个长度为0到15之间的0的模板
$ for n in 0..15; do printf "%15s :%02d: %s\n" $(printf "%0$nd" 0) $n $(rand $(printf "%0$nd" 0)); done
0 :00: 0
0 :01: 0
00 :02: 70
000 :03: 201
0000 :04: 9751
00000 :05: 62237
000000 :06: 262860
0000000 :07: 1365194
00000000 :08: 83953419
000000000 :09: 838521776
0000000000 :10: 2355011586
00000000000 :11: 95040136057
000000000000 :12: 511889225898
0000000000000 :13: 7441263049018
00000000000000 :14: 11895209107156
000000000000000 :15: 863219624761093
【讨论】:
【参考方案22】:你可以使用种子:
RANDOM=$(date +%s%N | cut -b10-19)
echo $(( $RANDOM % 100 + 1 ))
【讨论】:
以上是关于如何在 Bash 中生成随机数?的主要内容,如果未能解决你的问题,请参考以下文章