为什么将$RANDOM的输出导向管道会影响值?

4
以下是一段脚本,我期望它会生成同一对数字三次:
#! /usr/bin/env bash
RANDOM=5
echo " first" $RANDOM
echo "second" $RANDOM

echo

RANDOM=5
echo " first" $RANDOM
echo "second" $RANDOM

echo

RANDOM=5
echo "??????" $RANDOM | cat
echo " first" $RANDOM

相反,将输出导入管道会导致随机种子失效。为什么呢?

 first 18499
second 9909

 first 18499
second 9909

?????? 843
 first 18499

最终,我想要能够做到这样:
#! /usr/bin/env bash
n=3
for i in $( seq $n) ; do
    RANDOM=5
    echo $RANDOM | md5sum | cut -d' ' -f1
done

我该如何修改后面的脚本,以便每次运行时输出相同的结果?
2个回答

3

管道中的命令在子shell中执行,而子shell有自己独立的环境变量。 $RANDOM 在管道中更新,但此更新不会传播到父shell,这就是为什么从以下的echo输出18499的原因。

在这种情况下,您可以使用<<<重定向来避免使用管道。重定向不会创建子进程。

cat <<< "?????? $RANDOM"

或者

(md5sum | cut -d' ' -f1) <<< "$RANDOM"

1
@zee 这样做行不通,因为<( )确实会创建一个子shell。@JohnKugelman 我不明白为什么,但你的方法对我也不起作用(它的行为就像<<<字符串在子shell中被评估一样,尽管显然不应该这样)。我已经在bash v4.4.19、4.2.10和v3.2.57中进行了测试。 - Gordon Davisson
2
我尝试了许多Bash版本(https://gist.github.com/MatrixManAtYrService/2903762631599b2b7673c2cc1406140f),这个答案只适用于3.1和3.2.57。 - MatrixManAtYrService
为什么输出是可重复的,如果它应该是随机的?种子的含义是什么? - LMC
2
@LuisMuñoz $RANDOM 的输出基于先前的 $RANDOM 值,因此如果让您的 shell 初始化它,那么很难预测。如果您在某个时候自己设置它,那么它之后每次的行为都将相同(除非您调用新的 shell,这会为该 shell 创建一个新的不可预测的值)。它仍然不是一个非常好的随机源,因为一旦您知道它在某个时间的值,就可以预测其在随后的时间的值。如果您需要真正的随机性,/dev/urandom 是更好的选择。 - MatrixManAtYrService
重定向不会创建子shell。但是here-docs(和here-strings)会。或者至少可能会。你会如何实现here-docs?你可以将here-doc的文本写入临时文件,并将stdin重定向到临时文件。或者你可以fork bash进程,并将stdin重定向到一个管道中,该管道是子进程正在写入here-doc的地方。Posix没有指定机制(bash手册也没有记录它,据我所知),但很容易看出使用第二个机制的诱惑。将i = 0; echo $((++i)); echo $ii = 0; cat<<<$((++i)); echo $i进行对比。 - rici

1

由于@JohnKugelman的答案对我不起作用,这里提供另一种选择(它还有一个好处,即它将在不支持<<<的非bash shell中运行)。只需将临时变量分配给$RANDOM的结果,然后在子shell(或其他奇怪的上下文)中使用该临时变量:

#! /usr/bin/env bash
RANDOM=5
echo " first" $RANDOM
echo "second" $RANDOM

echo

RANDOM=5
tmprandom=$RANDOM
echo " first" $tmprandom | cat
echo "second" $RANDOM

输出(在bash v4下;v3使用不同的随机数生成器):

 first 18499
second 9909

 first 18499
second 9909

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接