kjo*_*kjo 19 command-line random
我正在寻找从命令行使用/dev/random
(或/dev/urandom
)的方法。特别是,我想知道如何使用这样的流stdin
来将随机数流写入stdout
(每行一个数字)。
我对机器架构本身支持的所有数字类型的随机数感兴趣。例如,对于 64 位体系结构,这些将包括 64 位有符号和无符号整数,以及 64 位浮点数。就范围而言,各种数字类型的最大范围都可以。
我知道如何使用 Perl、Python 等通用解释器来完成所有这些工作,但我想知道如何使用 shell 中的“更简单”的工具来完成这一切。(“更简单”是指“即使在非常小的 Unix 安装中也更有可能可用”。)
基本上,该问题减少了在命令行上将二进制数据转换为字符串表示的问题。(例如,这不行:printf '%f\n' $(head -c8 /dev/random)
.)
我正在寻找与外壳无关的答案。此外,之间的差别/dev/random
,并/dev/urandom
没有对这个问题很重要。我希望任何适用于一个的过程也适用于另一个,即使结果的语义可能不同。
我改编了EightBitTony的答案来产生toints
如下所示的功能等。
使用示例:
% < /dev/urandom toprobs -n 5
0.237616281778928
0.85578479125532
0.0330049682019756
0.798812391655243
0.138499033902422
Run Code Online (Sandbox Code Playgroud)
评论:
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'
}
Run Code Online (Sandbox Code Playgroud)
Eig*_*ony 27
您可以使用od
获得的数字出来的/dev/random
和/dev/urandom
。
例如,
2 字节无符号十进制整数,
$ od -vAn -N2 -tu2 < /dev/urandom
24352
Run Code Online (Sandbox Code Playgroud)
1 字节有符号十进制整数,
$ od -vAn -N1 -td1 < /dev/urandom
-78
Run Code Online (Sandbox Code Playgroud)
4 字节无符号十进制整数,
$ od -vAn -N4 -tu4 < /dev/urandom
3394619386
Run Code Online (Sandbox Code Playgroud)
man od
有关更多信息od
。
使用 Bash 内置程序比使用外部命令(如od
. 例如,以下是获取 60 位随机数的方法:
((RND=((RANDOM<<15|RANDOM)<<15|RANDOM)<<15|RANDOM))
Run Code Online (Sandbox Code Playgroud)
这将给出 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
Run Code Online (Sandbox Code Playgroud)
完毕。
(注意:在这个例子中,我决定一个 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
Run Code Online (Sandbox Code Playgroud)