【问题标题】:shell $RANDOM seed not honored in pipelines管道中不支持 shell $RANDOM 种子
【发布时间】:2014-06-05 01:08:30
【问题描述】:

这是一种我无法解释的奇怪行为。我想用 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 上试过。行为是一致的。谁能解释一下?

【问题讨论】:

  • 仅供参考 -- RANDOM 不是 POSIX sh 基线 shell 语言的一部分,而是 bash(可能还有其他现代 ksh 衍生产品)中可用的扩展。因此,这可能应该被标记为bash,而不是shell,并且您应该避免在以#!/bin/sh 开头的脚本中使用RANDOM,因为它不能保证与所有/bin/sh 解释器一起使用。

标签: bash shell random pipe seed


【解决方案1】:

管道,如echo $RANDOM | cat,创建subshells——从父进程派生的独立进程,但不会使用 exec()-family 调用替换为不同的可执行映像。您正在观察显式设置 RANDOM 的 shell 与从同一分支派生的子 shell 之间的行为差​​异。

您的解决方法是将$RANDOM 的评估从子shell 移到父shell(第一种情况),或者将显式种子集移到子shell(第二种情况)。

【讨论】:

  • +1;明确说明可能不直观的内容:管道echo $RANDOM | cat 的两个部分都在子shell 中运行。
【解决方案2】:

感谢Charles Duffy 指出正确的方向(子shell)。我在 bash 的 src 代码中发现,有文件 variable.c 。 $RANDOM 是一个“动态变量”,用于获取调用函数的值;并且当 $RANDOM 在子shell中首次评估时,该函数会重新播种随机生成器。

// 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 中的 $RANDOM 引用与父 shell 中的序列无关。

RANDOM=15
echo $RANDOM $RANDOM
RANDOM=15
echo $RANDOM | cat
echo $RANDOM

最后一行给出了 15 之后的第一个随机数。

【讨论】:

  • 子shell 中的更改不会影响父shell 并不奇怪——实现相反的行为(例如使用共享内存段)需要大量的努力/谨慎。不过,找到重新种子的代码是一个不错的选择; +1!
猜你喜欢
  • 2021-12-07
  • 1970-01-01
  • 2016-12-09
  • 1970-01-01
  • 2012-10-14
  • 1970-01-01
  • 2022-01-20
  • 2021-03-21
  • 2023-03-17
相关资源
最近更新 更多