Как генерировать случайные числа в оболочке BusyBox - PullRequest
4 голосов
/ 04 октября 2011

Как я могу генерировать случайные числа, используя AShell (bash с ограничениями)? Я использую двоичный файл BusyBox на устройстве, которое не имеет od или $RANDOM. Мое устройство имеет /dev/urandom и /dev/random.

Ответы [ 7 ]

6 голосов
/ 05 октября 2011

$RANDOM и od являются дополнительными функциями BusyBox, я предполагаю, что, учитывая ваш вопрос, они не включены в ваш двоичный файл.В комментарии вы упоминаете, что /dev/urandom присутствует, это хорошо, это означает, что вам нужно извлечь из него байты в удобной форме, а не намного более сложную задачу реализации генератора случайных чисел.Обратите внимание, что вы должны использовать /dev/urandom, а не /dev/random, см. Является ли ранд из / dev / urandom безопасным для ключа входа в систему? .

Если у вас tr или sed, вы можете прочитать байты из /dev/urandom и отбросить любой байт, который не является желательным символом.Вам также потребуется способ извлечения фиксированного количества байтов из потока: либо head -c (требующий включения FEATURE_FANCY_HEAD), либо dd (требующий компиляции dd).Чем больше байтов вы отбрасываете, тем медленнее будет этот метод.Тем не менее, генерация случайных байтов обычно довольно быстрая по сравнению с разветвлением и выполнением внешних двоичных файлов, поэтому отказ от многих из них не повредит.Например, следующий фрагмент даст случайное число от 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

Обратите внимание, что из-за буферизации tr будет обрабатывать на несколько байт больше, чем закончится ddсохраняя.tr BusyBox читает за один раз буфер (не менее 512 байт) и очищает свой выходной буфер всякий раз, когда входной буфер полностью обрабатывается, поэтому приведенная выше команда всегда будет читать не менее 512 байт из /dev/urandom (и очень редкопоскольку ожидаемый результат из 512 входных байтов составляет 20 десятичных цифр).

Если вам нужна уникальная печатная строка, просто отбросьте не-ASCII-символы и, возможно, некоторые раздражающие знаки пунктуации:

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

В этой ситуации я бы серьезно подумал о написании небольшой специализированной программы на Си.Вот тот, который читает четыре байта и выводит соответствующее десятичное число.Он не зависит от какой-либо функции libc, кроме оболочек для системных вызовов read и write, поэтому вы можете получить очень маленький двоичный файл.Поддержка переменной cap, переданной в виде десятичного целого числа в командной строке, оставляется в качестве упражнения;это будет стоить вам сотни байт кода (вам не о чем беспокоиться, если ваша цель достаточно велика для запуска 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;
}
1 голос
/ 04 мая 2016

Hexdump и dc доступны вместе с busybox.Используйте / dev / urandom для большей части случайности или / dev / random для лучшей случайности.Любой из этих параметров лучше, чем $ RANDOM, и работает быстрее, чем зацикливание при поиске печатаемых символов.

32-разрядное десятичное случайное число:

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

24-разрядное шестнадцатеричное случайное число:

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

Чтобы получить меньшие числа, измените формат строки формата hexdump и количество байтов, которые читает hexdump.

1 голос
/ 04 декабря 2015

Я попробовал первый фрагмент кода Жиля с BusyBox 1.22.1, и у меня есть несколько патчей, которые не вписались в комментарий:

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. Состояние цикла должно проверяться на наличие значения, превышающего максимальное значение, в противном случае будет выполнено 0 операций.
  2. я замолчал dd х stderr
  3. Удалены начальные нули, что может привести к неожиданностям в контекстах, которые интерпретируются как восьмеричные (например, $(( )))
1 голос
/ 03 декабря 2012
</dev/urandom sed 's/[^[:digit:]]\+//g' | head -c10
1 голос
/ 04 октября 2011

/ dev / random или / dev / urandom могут присутствовать.

Другой вариант - написать небольшую программу на C, которая вызывает srand (), затем rand ().

0 голосов
/ 21 сентября 2018

Улучшен ответ, приведенный выше, для более простой версии, которая также работает действительно быстрее, все еще совместима с 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

}

Использовать с

echo $(PoorMansRandomGenerator 5)
0 голосов
/ 04 июня 2018

Попытка решения escitalopram не сработала на busybox v1.29.0, но вдохновила меня на выполнение функции.

s Я действительно придумал портативную функцию генерации случайных чисел , которая запрашивает количество цифр и должна работать довольно хорошо ( протестировано на 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
}

Следующее дает число между 1000 и 9999 echo $ (PoorMansRandomGenerator 4)

...