这是一种我无法解释的奇怪行为。我想使用shell生成可预测的随机数序列。我使用带有种子的$ RANDOM。这是一个测试程序。
RANDOM=15
echo $RANDOM
每次运行时都会给出相同的数字。但是如果我在这个程序中添加一个管道,它每次都会给出不同的结果。请尝试以下简化程序。
RANDOM=15
echo $RANDOM | cat
我找到了2个问题的修复程序(使其可预测),但仍无法解释原因。
修复1
RANDOM=15
x=$RANDOM
echo $x | cat
修复2
(RANDOM=15
echo $RANDOM) | cat
我试过Linux和Mac。行为是一致的。有人可以解释一下吗?
答案 0 :(得分:7)
管道,如在echo $RANDOM | cat
中,创建subshells - 从父代分叉的单独进程,但不使用exec() - 系列调用替换为不同的可执行映像。您正在观察明确设置RANDOM
的shell与从中分叉的子shell之间的行为差异。
您的解决方法是将$RANDOM
的子评估从子shell移到父级(第一种情况),或将显式种子集移动到子shell中(第二种情况)。
答案 1 :(得分:2)
感谢Charles Duffy指向正确的方向(子shell)。我在bash的src代码中找到了文件variable.c。 $ RANDOM是一个"动态变量",用于获取调用函数的值;当在子shell中首次计算$ RANDOM时,函数重新对随机生成器进行种子化。
// from bash-4.3/variables.c
int
get_random_number ()
{
int rv, pid;
/* Reset for command and process substitution. */
pid = getpid ();
if (subshell_environment && seeded_subshell != pid)
{
seedrand (); // <<<<==== re-seed!
seeded_subshell = pid;
}
do
rv = brand ();
while (rv == last_random_value);
return rv;
}
Seed是一个静态变量,因此每个shell都有自己的副本。子shell中的重新种子对父级没有影响。这是另一个测试用例,显示子shell中的$ RANDOM引用与父shell中的序列无关。
RANDOM=15
echo $RANDOM $RANDOM
RANDOM=15
echo $RANDOM | cat
echo $RANDOM
最后一行给出了15之后的第一个随机数。