Ответ 1
shuf -i 2000-65000 -n 1
Наслаждайтесь!
Изменить: диапазон включен.
Мне нужно создать произвольный номер порта между 2000-65000
из оболочки script. Проблема в том, что $RANDOM
- это 15-битное число, поэтому я застрял!
PORT=$(($RANDOM%63000+2001))
будет работать хорошо, если бы не ограничение размера.
Есть ли у кого-нибудь пример того, как я могу это сделать, возможно, извлекая что-то из /dev/urandom
и получая его в пределах диапазона?
shuf -i 2000-65000 -n 1
Наслаждайтесь!
Изменить: диапазон включен.
В Mac OS X и FreeBSD вы также можете использовать jot:
jot -r 1 2000 65000
В соответствии с bash справочной страницей $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; вам все равно придется делать обрезку.
Вот еще один. Я думал, что это сработает почти во всем, но сортировка случайного варианта недоступна в моей панели centos на работе.
seq 2000 65000 | sort -R | head -n 1
Вы можете сделать это
cat /dev/urandom|od -N2 -An -i|awk -v f=2000 -v r=65000 '{printf "%i\n", f + r * $1 / 65536}'
Если вам нужно больше подробностей, смотрите Генератор случайных чисел в скрипте оболочки.
Если вы не эксперт bash и не хотите, чтобы он попадал в переменную в Linux bash script, попробуйте следующее:
VAR=$(shuf -i 200-700 -n 1)
Это дает вам диапазон от 200 до 700 в $VAR
включительно.
$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
Bash документация говорит, что при каждом обращении к $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 "$2" && "$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 $1 finished after $i iterations in $(($(date +%s) - start))s."
out="ports.$1.txt"
[[ "$1" != "0" ]] && out="ports/$out"
echo "${ports[@]}" > "$out"
}
say-total() {
generated_ports=$(cat "[email protected]" | tr ' ' '\n' | \sed -E s/'^([0-9]{4})$'/'0\1'/)
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"
[[ "$1" != "--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.
То же самое с ruby:
ZZZ=$(ruby -e 'puts rand(20..65)')
echo $ZZZ #=> 56
Или на OS-X для меня работает следующее:
$ gsort --random-sort
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 $1%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