简体   繁体   English

如何在 BusyBox shell 中生成随机数

[英]How to generate random numbers in the BusyBox shell

How can I generate random numbers using AShell (restricted bash)?如何使用 AShell(受限 bash)生成随机数? I am using a BusyBox binary on the device which does not have od or $RANDOM .我在没有od$RANDOM的设备上使用 BusyBox 二进制文件。 My device has /dev/urandom and /dev/random .我的设备有/dev/urandom/dev/random

$RANDOM and od are optional features in BusyBox, I assume given your question that they aren't included in your binary. $RANDOMod是 BusyBox 中的可选功能,鉴于您的问题,我假设它们不包含在您的二进制文件中。 You mention in a comment that /dev/urandom is present, that's good, it means what you need to do is retrieve bytes from it in a usable form, and not the much more difficult problem of implementing a random number generator.您在评论中提到/dev/urandom存在,这很好,这意味着您需要做的是以可用的形式从中检索字节,而不是实现随机数生成器的困难得多的问题。 Note that you should use /dev/urandom and not /dev/random , see Is a rand from /dev/urandom secure for a login key?请注意,您应该使用/dev/urandom而不是/dev/random ,请参阅来自 /dev/urandom 的 rand 是否安全用于登录密钥? . .

If you have tr or sed , you can read bytes from /dev/urandom and discard any byte that isn't a desirable character.如果您有trsed ,则可以从/dev/urandom读取字节并丢弃任何不需要的字节。 You'll also need a way to extract a fixed number of bytes from a stream: either head -c (requiring FEATURE_FANCY_HEAD to be enabled) or dd (requiring dd to be compiled in).您还需要一种从流中提取固定数量字节的方法: head -c (需要启用FEATURE_FANCY_HEAD )或dd (需要编译dd )。 The more bytes you discard, the slower this method will be.丢弃的字节越多,此方法的速度就越慢。 Still, generating random bytes is usually rather fast in comparison with forking and executing external binaries, so discarding a lot of them isn't going to hurt much.尽管如此,与分叉和执行外部二进制文件相比,生成随机字节通常相当快,因此丢弃大量随机字节不会造成太大伤害。 For example, the following snippet will produce a random number between 0 and 65535:例如,以下代码段将生成 0 到 65535 之间的随机数:

n=65536
while [ $n -ge 65536 ]; do
  n=1$(</dev/urandom tr -dc 0-9 | dd bs=5 count=1 2>/dev/null)
  n=$((n-100000))
done

Note that due to buffering, tr is going to process quite a few more bytes than what dd will end up keeping.请注意,由于缓冲, tr将处理比dd最终保留的字节多得多的字节。 BusyBox's tr reads a bufferful (at least 512 bytes) at a time, and flushes its output buffer whenever the input buffer is fully processed, so the command above will always read at least 512 bytes from /dev/urandom (and very rarely more since the expected take from 512 input bytes is 20 decimal digits). BusyBox 的tr读取一个缓冲区(至少 512 个字节),并在输入缓冲区被完全处理时刷新其输出缓冲区,因此上面的命令将始终从/dev/urandom读取至少 512 个字节(并且很少更多,因为512 个输入字节的预期取值为 20 个十进制数字)。

If you need a unique printable string, just discard non-ASCII characters, and perhaps some annoying punctuation characters:如果您需要一个唯一的可打印字符串,只需丢弃非 ASCII 字符,也许还有一些烦人的标点符号:

nonce=$(</dev/urandom tr -dc A-Za-z0-9-_ | head -c 22)

In this situation, I would seriously consider writing a small, dedicated C program.在这种情况下,我会认真考虑编写一个小的、专用的 C 程序。 Here's one that reads four bytes and outputs the corresponding decimal number.这是一个读取四个字节并输出相应的十进制数的方法。 It doesn't rely on any libc function other than the wrappers for the system calls read and write , so you can get a very small binary.除了系统调用readwrite的包装外,它不依赖于任何 libc 函数,因此您可以获得一个非常小的二进制文件。 Supporting a variable cap passed as a decimal integer on the command line is left as an exercise;支持在命令行上作为十进制整数传递的变量 cap 留作练习; it'll cost you hundreds of bytes of code (not something you need to worry about if your target is big enough to run Linux).这将花费您数百字节的代码(如果您的目标大到足以运行 Linux,则无需担心)。

#include <stddef.h>
#include <unistd.h>
int main () {
    int n;
    unsigned long x = 0;
    unsigned char buf[4];
    char dec[11]; /* Must fit 256^sizeof(buf) in decimal plus one byte */
    char *start = dec + sizeof(dec) - 1;
    n = read(0, buf, sizeof(buf));
    if (n < (int)sizeof(buf)) return 1;
    for (n = 0; n < (int)sizeof(buf); n++) x = (x << 8 | buf[n]);
    *start = '\n';
    if (x == 0) *--start = '0';
    else while (x != 0) {
        --start;
        *start = '0' + (x % 10);
        x = x / 10;
    }
    while (n = write(1, start, dec + sizeof(dec) - start),
           n > 0 && n < dec + sizeof(dec) - start) {
        start += n;
    }
    return n < 0;
}
</dev/urandom sed 's/[^[:digit:]]\+//g' | head -c10

I Tried Gilles' first snippet with BusyBox 1.22.1 and I have some patches, which didn't fit into a comment:我用 BusyBox 1.22.1 尝试了 Gilles 的第一个片段,我有一些补丁,不适合评论:

while [ $n -gt 65535 ]; do
    n=$(</dev/urandom tr -dc 0-9 | dd bs=5 count=1 2>/dev/null | sed -e 's/^0\+//' )
done
  1. The loop condition should check for greater than the maximum value, otherwise there will be 0 executions.循环条件应检查是否大于最大值,否则将执行 0 次。
  2. I silenced dd 's stderr我使ddstderr静音
  3. Leading zeros removed, which could lead to surprises in contexts where interpreted as octal (eg $(( )) )删除了前导零,这可能会导致在解释为八进制的上下文中出现意外(例如$(( ))

Hexdump and dc are both available with busybox.十六进制转储和 dc 都可用于 busybox。 Use /dev/urandom for mostly random or /dev/random for better random.将 /dev/urandom 用于大部分随机或 /dev/random 以获得更好的随机。 Either of these options are better than $RANDOM and are both faster than looping looking for printable characters.这些选项中的任何一个都比 $RANDOM 更好,并且都比循环查找可打印字符更快。

32-bit decimal random number: 32位十进制随机数:

CNT=4
RND=$(dc 10 o 0x$(hexdump -e '"%02x" '$CNT' ""' -n $CNT /dev/random) p)

24-bit hex random number: 24 位十六进制随机数:

CNT=3
RND=0x$(hexdump -e '"%02x" '$CNT' ""' -n $CNT /dev/random)

To get smaller numbers, change the format of the hexdump format string and the count of bytes that hexdump reads.要获得更小的数字,请更改 hexdump 格式字符串的格式和 hexdump 读取的字节数。

/dev/random or /dev/urandom are likely to be present. /dev/random 或 /dev/urandom 可能存在。

Another option is to write a small C program that calls srand(), then rand().另一种选择是编写一个小的 C 程序,先调用 srand(),然后调用 rand()。

Trying escitalopram's solution didn't work on busybox v1.29.0 but inspired me doing a function.尝试艾司西酞普兰的解决方案在 busybox v1.29.0 上不起作用,但启发了我做一个功能。

sI did actually come up with a portable random number generation function that asks for the number of digits and should work fairly well ( tested on Linux, WinNT10 bash, Busybox and msys2 so far ).我确实想出了一个可移植的随机数生成函数,它要求输入数字的数量并且应该可以很好地工作(到目前为止在 Linux、WinNT10 bash、Busybox 和 msys2 上测试过)。

# Get a random number on Windows BusyBox alike, also works on most Unixes
function PoorMansRandomGenerator {
    local digits="${1}"     # The number of digits of the number to generate

    local minimum=1
    local maximum
    local n=0

    if [ "$digits" == "" ]; then
        digits=5
    fi

    # Minimum already has a digit
    for n in $(seq 1 $((digits-1))); do
        minimum=$minimum"0"
        maximum=$maximum"9"
    done
    maximum=$maximum"9"

    #n=0; while [ $n -lt $minimum ]; do n=$n$(dd if=/dev/urandom bs=100 count=1 2>/dev/null | tr -cd '0-9'); done; n=$(echo $n | sed -e 's/^0//')
    # bs=19 since if real random strikes, having a 19 digits number is not supported
    while [ $n -lt $minimum ] || [ $n -gt $maximum ]; do
        if [ $n -lt $minimum ]; then
            # Add numbers
            n=$n$(dd if=/dev/urandom bs=19 count=1 2>/dev/null | tr -cd '0-9')
            n=$(echo $n | sed -e 's/^0//')
            if [ "$n" == "" ]; then
                n=0
            fi
        elif [ $n -gt $maximum ]; then
            n=$(echo $n | sed 's/.$//')
        fi
    done
    echo $n
}

The following gives a number between 1000 and 9999 echo $(PoorMansRandomGenerator 4)下面给出了一个 1000 到 9999 之间的数字 echo $(PoorMansRandomGenerator 4)

Improved the above reply to a more simpler version,that also runs really faster, still compatible with Busybox, Linux, msys and WinNT10 bash.将上面的回复改进为更简单的版本,运行速度也更快,仍然兼容 Busybox、Linux、msys 和 WinNT10 bash。

function PoorMansRandomGenerator {
    local digits="${1}" # The number of digits to generate
    local number

    # Some read bytes can't be used, se we read twice the number of required bytes
    dd if=/dev/urandom bs=$digits count=2 2> /dev/null | while read -r -n1 char; do
            number=$number$(printf "%d" "'$char")
            if [ ${#number} -ge $digits ]; then
                    echo ${number:0:$digits}
                    break;
            fi
    done
}

Use with

echo $(PoorMansRandomGenerator 5)

​​​​​​​​​​​​​

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM