【问题标题】:Random number between range in shellshell中范围之间的随机数
【发布时间】:2021-02-24 04:59:22
【问题描述】:

如何在 sh(/bin/sh,而不是 bash)中生成 0-60 之间的随机数?这是卫星箱,没有$RANDOM变量,其他货[cksum,od(od -vAn -N4 -tu4/urandom)]。

我想随机化一个 crontab 作业的时间。

【问题讨论】:

    标签: linux shell random


    【解决方案1】:

    如果你有 tr、head 和 /dev/urandom,你可以这样写:

    tr -cd 0-9 </dev/urandom | head -c 3
    

    那么你必须使用余数运算符来输入 0-60 范围。

    【讨论】:

    • 啊。我以这样的方式阅读了这个问题,让人相信 /dev/urandom 不存在。这是一个很好的解决方案。
    • 我收到tr: illegal byte sequence
    • 在命令之前尝试使用 LANG=C(来源:stackoverflow.com/questions/11287564/… 用于 sed 但希望也适用于您)
    【解决方案2】:

    使用纳秒的系统时间怎么样?

    date +%N
    

    这里不需要加密有用的数字。

    根据/bin/sh 的版本,您可能可以这样做:

    $((date +%N%60))

    如果它不支持$(())语法,但你有dc,你可以试试:

    dc -e `date +%N`' 60 % p'
    

    不知道哪个操作系统、/bin/sh 的版本或什么 工具是可用的,很难想出一个保证有效的解决方案。

    【讨论】:

    • 不幸的是没有使用日期,没有 dc.. /bin/sh --help /bin/sh --help BusyBox v1.15.3 (2010-07-21 18:00:33 CEST ) 多调用二进制
    【解决方案3】:

    你有 awk 吗?您可以调用 awk 的 rand() 函数。例如:

    awk 'BEGIN { printf("%d\n",rand()*60)  }' < /dev/null
    

    【讨论】:

    • @Adrian:您必须为随机数生成器播种:awk 'BEGIN { srand(); printf("%d\n",rand()*60) }'。如果只有BEGIN 子句,则不需要重定向。
    • @Dennis,如果您计划每秒多次调用 rand() ,这是正确的。否则,srand() 应该在时间自动播种
    • awk 'BEGIN {print rand(), rand()}'; sleep 3; awk 'BEGIN {print rand(), rand()}' 两次为 GNU awk 和 BusyBox awk 提供了相同的一对值(不同版本的值不同)。如果我包含srand(),即使我删除了sleep,值也会有所不同。必须显式地播种随机数生成器并不罕见。部分原因是您可以在测试期间获得可预测的序列。
    【解决方案4】:

    我知道这篇文章很旧,但建议的答案不会生成统一的无偏随机数。接受的答案基本上是这样的:

    % echo $(( $(tr -cd 0-9 </dev/urandom | head -c 3) % 60))
    

    这个建议的问题是,通过从/dev/urandom中选择一个3位数字,范围是0-999,一共1000个数字。但是,1,000 并不能平均分为 60。因此,您将偏向于生成 0-959,略高于 960-999。

    第二个答案虽然创造性地使用时钟中的纳秒,但也存在同样的偏见:

    % echo $(( $(date +%N) % 60 ))
    

    纳秒的范围是 0-999,999,999,即 10 亿个数字。因此,如果您将该结果除以 60,您将再次倾向于生成 0-999,999,959,略多于 999,999,960-999,999,999。

    其余所有答案都是相同的偏向非均匀生成。

    要生成 0-59 范围内的无偏均匀随机数(如果他试图随机化 crontab(1) 条目,我认为他的意思是他的意思而不是 0-60),我们需要强制输出为60 的倍数。

    首先,我们将生成一个介于 0 和 4294967295 之间的 32 位随机数:

    % RNUM=$(od -An -N4 -tu2 /dev/urandom | awk '{print $1}')
    

    现在我们将强制范围在 $MIN 和 4294967295 之间,即 60 的倍数:

    % MIN=$((2**32 % 60)) # 16
    

    这意味着:

    4294967296 - 16 = 4294967280
    4294967280 / 60 = 71582788.0
    

    换句话说,我的 [16, 4294967295] 范围正好是 60 的倍数。因此,我在该范围内生成的每个数字,然后除以 60,与任何其他数字一样可能。因此,我有一个数字 0-59(如果加 1,则为 1-60)的无偏生成器。

    剩下要做的就是确保我的号码在 16 到 4294967295 之间。如果我的号码小于 16,那么我需要生成一个新号码:

    % while [ $RNUM -lt $MIN ]; do RNUM=$(od -An -N1 -tu2 /dev/urandom); done
    % MINUTE=$(($RNUM % 60))
    

    所有东西都放在一起复制/粘贴好东西:

    #!/bin/bash
    RNUM=$(od -An -N4 -tu2 /dev/urandom | awk '{print $1}')
    MIN=$((2**32 % 60))
    while [ $RNUM -lt $MIN ]; do RNUM=$(od -An -N1 -tu2 /dev/urandom); done
    MINUTE=$(($RNUM % 60))
    

    【讨论】:

      【解决方案5】:
      值=`od -An -N2 -tu2 /dev/urandom`
      分钟=`expr $value % 60`

      种子将介于 0 和 65535 之间,这不是 60 的偶数倍,因此选择 0-15 分钟的 ob 的机会稍大一些,但差异可能并不重要。

      如果你想达到完美,使用“od -An -N1 -tu1”并循环直到值小于240。

      用busybox od测试。

      【讨论】:

        【解决方案6】:

        当生成的数字以 0 开头并且其他数字大于 7 时,请注意错误,因为它被解释为八进制,我建议:

        tr -cd 0-9 </dev/urandom | head -c 4 | sed -e 's/^00*//
        

        特别是如果您想进一步处理它,例如建立一个范围:

        RANDOM=`tr -cd 0-9 </dev/urandom | head -c 4 | sed -e 's/^00*//'`
        RND50=$((($RANDOM%50)+1))   // random number between 1 and 50
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-03-31
          • 1970-01-01
          • 1970-01-01
          • 2011-08-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多