Случайное число из диапазона в скрипте Bash
мне нужно сгенерировать случайный номер порта между 2000-65000 из сценария оболочки. Проблема в том $RANDOM это 15-разрядное число, так что я застрял!
PORT=$(($RANDOM%63000+2001)) будет работать хорошо, если это не было для ограничения размера.
у кого-нибудь есть пример того, как я могу это сделать, может быть, извлекая что-то из /dev/urandom и получить его в пределах диапазона?
15 ответов:
согласно странице bash man,
$RANDOMраспределяется между 0 и 32767; то есть это беззнаковое 15-битное значение. Предполагая, что$RANDOMравномерно распределено, вы можете создать равномерно распределенное беззнаковое 30-битное целое число следующим образом:$(((RANDOM<<15)|RANDOM))так как ваш диапазон не является силой 2, простая операция по модулю будет только почти дать вам равномерное распределение, но с 30-битным диапазоном ввода и менее чем 16-битным диапазоном вывода, Как у вас в вашем случае, это должно быть достаточно близко:
PORT=$(( ((RANDOM<<15)|RANDOM) % 63001 + 2000 ))
и вот один с Python
randport=$(python -S -c "import random; print random.randrange(2000,63000)")и один с awk
awk 'BEGIN{srand();print int(rand()*(63000-2000))+2000 }'
самый простой общий способ, который приходит на ум, - это perl one-liner:
perl -e 'print int(rand(65000-2000)) + 2000'вы всегда можете просто использовать два номера:
PORT=$(($RANDOM + ($RANDOM % 2) * 32768))вы все еще должны закрепить к вашему ряду. Это не общий метод N-битных случайных чисел, но он будет работать для вашего случая, и все это внутри bash.
Если вы хотите быть очень милым и читать из /dev / urandom, вы можете сделать это:
od -A n -N 2 -t u2 /dev/urandomэто будет читать два байта и печатать их как unsigned int; вы все еще нужно сделать свою вырезку.
вот еще одна. Я думал, что это будет работать на что угодно, но случайная опция sort недоступна на моем CentOS box на работе.
seq 2000 65000 | sort -R | head -n 1
Если вы не эксперт bash и хотели получить это в переменную в скрипте bash на базе Linux, попробуйте следующее:
VAR=$(shuf -i 200-700 -n 1)это дает вам диапазон от 200 до 700 в
$VARвключительно.
вы можете сделать это
cat /dev/urandom|od -N2 -An -i|awk -v f=2000 -v r=65000 '{printf "%i\n", f + r * / 65536}'Если вам нужно больше деталей см. Генератор Случайных Чисел Сценария Оболочки.
Баш документации написано каждый раз
$RANDOMссылается, возвращается случайное число от 0 до 32767. Если мы суммируем две последовательные ссылки, мы получим значения от 0 до 65534, что охватывает желаемый диапазон 63001 возможностей для случайного числа между 2000 и 65000.чтобы настроить его на точный диапазон, мы используем сумму по модулю 63001, которая даст нам значение от 0 до 63000. Это в свою очередь просто нужно увеличить к 2000 году, чтобы обеспечить желаемое случайное число, между 2000 и 65000. Это можно резюмировать следующим образом:
port=$((((RANDOM + RANDOM) % 63001) + 2000))тестирование
# Generate random numbers and print the lowest and greatest found test-random-max-min() { max=2000 min=65000 for i in {1..10000}; do port=$((((RANDOM + RANDOM) % 63001) + 2000)) echo -en "\r$port" [[ "$port" -gt "$max" ]] && max="$port" [[ "$port" -lt "$min" ]] && min="$port" done echo -e "\rMax: $max, min: $min" } # Sample output # Max: 64990, min: 2002 # Max: 65000, min: 2004 # Max: 64970, min: 2000правильность расчета
вот полный, грубая сила тест на правильность расчета. Эта программа просто пытается генерировать все 63001 различных возможностей случайным образом, используя расчет в тесте. Элемент
--jobsпараметр должен заставить его работать быстрее, но он не детерминирован (всего генерируемые возможности могут быть ниже 63001).test-all() { start=$(date +%s) find_start=$(date +%s) total=0; ports=(); i=0 rm -f ports/ports.* ports.* mkdir -p ports while [[ "$total" -lt "" && "$all_found" != "yes" ]]; do port=$((((RANDOM + RANDOM) % 63001) + 2000)); i=$((i+1)) if [[ -z "${ports[port]}" ]]; then ports["$port"]="$port" total=$((total + 1)) if [[ $((total % 1000)) == 0 ]]; then echo -en "Elapsed time: $(($(date +%s) - find_start))s \t" echo -e "Found: $port \t\t Total: $total\tIteration: $i" find_start=$(date +%s) fi fi done all_found="yes" echo "Job finished after $i iterations in $(($(date +%s) - start))s." out="ports..txt" [[ "" != "0" ]] && out="ports/$out" echo "${ports[@]}" > "$out" } say-total() { generated_ports=$(cat "$@" | tr ' ' '\n' | \sed -E s/'^([0-9]{4})$'/'0'/) echo "Total generated: $(echo "$generated_ports" | sort | uniq | wc -l)." } total-single() { say-total "ports.0.txt"; } total-jobs() { say-total "ports/"*; } all_found="no" [[ "" != "--jobs" ]] && test-all 0 63001 && total-single && exit for i in {1..1000}; do test-all "$i" 40000 & sleep 1; done && wait && total-jobsдля определения количества итераций, необходимых для получения заданной вероятности
p/qиз всех 63001 возможностей, которые были созданы, я считаю, что мы можем использовать выражение ниже. Например, вот расчет для вероятности больше 1/2 и здесь для более 9/10.
$RANDOM- это число от 0 до 32767. Вы хотите порт между 2000 и 65000. Это 63001 возможных порта. Если мы будем придерживаться значений$RANDOM + 2000между 2000 и 33500, мы покрываем ряд 31501 портов. Если мы перевернем монету, а затем условно добавим 31501 к результату, мы можем получить больше портов, от 33501 до 65001. Тогда, если мы просто отбросим 65001, мы получим точное покрытие, необходимое, с равномерным распределением вероятности для всех порты, кажется.random-port() { while [[ not != found ]]; do # 2000..33500 port=$((RANDOM + 2000)) while [[ $port -gt 33500 ]]; do port=$((RANDOM + 2000)) done # 2000..65001 [[ $((RANDOM % 2)) = 0 ]] && port=$((port + 31501)) # 2000..65000 [[ $port = 65001 ]] && continue echo $port break done }тестирование
i=0 while true; do i=$((i + 1)) printf "\rIteration $i..." printf "%05d\n" $(random-port) >> ports.txt done # Then later we check the distribution sort ports.txt | uniq -c | sort -r
PORT=$(($RANDOM%63000+2001))- Это близко к тому, что вы хотите, я думаю.
PORT=$(($RANDOM$RANDOM$RANDOM%63000+2001))получает вокруг ограничения размера, который беспокоит вас. Поскольку bash не делает различий между числовой переменной и строковой переменной, это работает отлично. "Число"$RANDOMможно объединить как строку, а затем использовать в качестве числа в вычислении. Потрясающе!
вы можете получить случайное число с помощью
urandom
head -200 /dev/urandom | cksumвыход:
3310670062 52870чтобы получить одну часть вышеуказанного числа.
head -200 /dev/urandom | cksum | cut -f1 -d " "тогда выход
3310670062для того чтобы соотвествовать ваш,
head -200 /dev/urandom |cksum | cut -f1 -d " " | awk '{print %63000+2001}'
Так я обычно генерирую случайные числа. Затем я использую "NUM_1" в качестве переменной для номера порта, который я использую. Вот краткий пример скрипта.
#!/bin/bash clear echo 'Choose how many digits you want for port# (1-5)' read PORT NUM_1="$(tr -dc '0-9' </dev/urandom | head -c $PORT)" echo "$NUM_1" if [ "$PORT" -gt "5" ] then clear echo -e "\x1b[31m Choose a number between 1 and 5! \x1b[0m" sleep 3 clear exit 0 fi

Comments