shell $ RANDOM种子在管道中没有兑现

时间:2014-06-05 01:08:31

标签: bash shell random pipe seed

这是一种我无法解释的奇怪行为。我想使用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。行为是一致的。有人可以解释一下吗?

2 个答案:

答案 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之后的第一个随机数。