我正在寻找从命令行使用/dev/random
(或) 的方法。/dev/urandom
特别是,我想知道如何使用这样的流将stdin
随机数流写入stdout
(每行一个数字)。
我对机器架构本身支持的所有数字类型的随机数感兴趣。例如,对于 64 位体系结构,这些将包括 64 位有符号和无符号整数以及 64 位浮点数。就范围而言,各种数字类型的最大范围就可以了。
我知道如何使用 Perl、Python 等通用解释器来完成所有这些工作,但我想知道如何使用 shell 中的“更简单”工具来完成这一切。 (我所说的“更简单”是指“即使在非常小的 Unix 安装中也更有可能可用”。)
基本上,这个问题减少了在命令行上将二进制数据转换为其字符串表示形式的问题。 (例如,这不行:printf '%f\n' $(head -c8 /dev/random)
。)
我正在寻找与 shell 无关的答案。此外,/dev/random
和之间的区别/dev/urandom
对于这个问题并不重要。我希望任何适用于其中一个的过程也适用于另一个,即使结果的语义可能不同。
我改编了EightBitTony的答案来生成如下所示的功能toints
等。
使用示例:
% < /dev/urandom toprobs -n 5
0.237616281778928
0.85578479125532
0.0330049682019756
0.798812391655243
0.138499033902422
评论:
- 我使用
hexdump
而不是od
因为它给了我一种更简单的方法来按照我想要的方式格式化输出; - 但令人烦恼的是,
hexdump
不支持 64 位整数(wtf???); - 函数的接口需要改进(例如,它们应该接受
-n5
以及-n 5
),但考虑到我可怜的 shell 编程技巧,这是我能快速组合起来的最好的。 (一如既往,欢迎提出意见/改进。)
我从这个练习中得到的最大惊喜是发现如何难的就是在shell上编程最基本的数字内容(例如读取十六进制浮点值,或获取最大本机浮点值)...
_tonums () {
local FUNCTION_NAME=$1 BYTES=$2 CODE=$3
shift 3
local USAGE="Usage: $FUNCTION_NAME [-n <INTEGER>] [FILE...]"
local -a PREFIX
case $1 in
( -n ) if (( $# > 1 ))
then
PREFIX=( head -c $(( $2 * $BYTES )) )
shift 2
else
echo $USAGE >&2
return 1
fi ;;
( -* ) echo $USAGE >&2
return 1 ;;
( * ) PREFIX=( cat ) ;;
esac
local FORMAT=$( printf '"%%%s\\n"' $CODE )
$PREFIX "$@" | hexdump -ve $FORMAT
}
toints () {
_tonums toints 4 d "$@"
}
touints () {
_tonums touints 4 u "$@"
}
tofloats () {
_tonums tofloats 8 g "$@"
}
toprobs () {
_tonums toprobs 4 u "$@" | perl -lpe '$_/=4294967295'
}
答案1
您可以使用从和od
中获取数字。/dev/random
/dev/urandom
例如,
2字节无符号十进制整数,
$ od -vAn -N2 -tu2 < /dev/urandom
24352
1 字节有符号十进制整数,
$ od -vAn -N1 -td1 < /dev/urandom
-78
4字节无符号十进制整数,
$ od -vAn -N4 -tu4 < /dev/urandom
3394619386
man od
了解更多信息od
。
答案2
一些 shell(例如bash(1)
)有一个$RANDOM
给出随机数的“变量”。
答案3
使用 Bash 内置命令比使用诸如od
.例如,以下是获取 60 位随机数的方法:
((RND=((RANDOM<<15|RANDOM)<<15|RANDOM)<<15|RANDOM))
这将给出 0 到 1,152,921,504,606,846,975 范围内的随机数。您可以通过模除法将其缩小到您想要的任何范围。
作为一个实际的例子,假设我想从硬盘中读取一个随机扇区,以将磁盘从待机状态唤醒。我可以做这个:
#!/bin/bash
DEV=/dev/sda # Let's say that this is the device we want to spin up.
# Get the device geometry...
read -d- SIZE64 BS <<<$(blockdev --getsize64 --getbsz $DEV)
((SECTORS=SIZE64/BS)) # The total number of $BS-sized sectors on that device.
((RND=(RANDOM<<15|RANDOM)<<15|RANDOM)) # Generate a 45-bit random number.
((SECT=RND%SECTORS)) # The random sector that we will now read...
dd if=$DEV of=/dev/null bs=$BS skip=$SECT count=1 >/dev/null 2>&1
完毕。
(注意:在这个例子中,我认为 45 位随机整数就足够了,而且比 60 位随机整数快一点点。)
更新:
为上述速度主张提供一些定量支持:
~# time for i in {1..10000} ;do RND=$(od -An -N7 -tu8 /dev/urandom) ;done
real 0m45.647s
user 0m17.540s
sys 0m28.807s
~# time for i in {1..10000} ;do ((RND=((RANDOM<<15|RANDOM)<<15|RANDOM)<<15|RANDOM)) ;done
real 0m0.112s
user 0m0.105s
sys 0m0.007s
答案4
从版本 5.1 或 5.2 开始,bash 有一个$SRANDOM
提供 32 位随机数的“变量”。