Bash脚本中范围内的随机数

我需要从shell脚本生成2000-65000之间的随机端口号。问题是$RANDOM是一个15位的数字,所以我卡住了!

如果没有大小限制,PORT=$(($RANDOM%63000+2001))将很好地工作。

有没有人有一个例子,我可以这样做,也许从/dev/urandom提取一些东西,并得到它在一个范围内?

230539 次浏览

我能想到的最简单的方法是perl一行代码:

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输出;你还是得剪。

shuf -i 2000-65000 -n 1

享受吧!

编辑:范围是包含的。

这是一个用Python写的

randport=$(python -S -c "import random; print random.randrange(2000,63000)")

还有一个是awk

awk 'BEGIN{srand();print int(rand()*(63000-2000))+2000 }'

在Mac OS X和FreeBSD上,你也可以使用jot:

jot -r 1  2000 65000

这是另一个。我原以为它对任何东西都适用,但我工作时的centos盒子上没有sort的随机选项。

 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}'

如果需要更多细节,请参阅Shell脚本随机数生成器

或在OS-X以下工作为我:

$ gsort --random-sort

PORT=$(($RANDOM%63000+2001))接近你想要的,我认为。

PORT=$(($RANDOM$RANDOM$RANDOM%63000+2001))绕过了困扰你的大小限制。由于bash不区分数字变量和字符串变量,因此这工作得非常好。“数字”$RANDOM可以像字符串一样连接,然后在计算中用作数字。神奇的!

每次引用$RANDOM时,返回一个0到32767之间的随机数。如果我们将两个连续的引用相加,我们会得到从0到65534的值,这涵盖了一个在2000到65000之间的随机数的63001种可能性的理想范围。

为了将其调整到精确的范围,我们使用对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 "$@" | 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

为了确定需要多少次迭代才能得到所有63001种可能性的给定概率p/q,我相信我们可以使用下面的表达式。例如,这里是大于1/2的概率的计算这里大于9/10

Expression

根据bash手册页,$RANDOM分布在0到32767之间;也就是说,它是一个无符号的15位值。假设$RANDOM是均匀分布的,你可以创建一个均匀分布的30位无符号整数,如下所示:

$(((RANDOM<<15)|RANDOM))

因为你的范围不是2的幂,一个简单的模运算只能几乎给你一个均匀的分布,但是如果输入范围是30位,输出范围小于16位,就像你的例子一样,这应该足够接近了:

PORT=$(( ((RANDOM<<15)|RANDOM) % 63001 + 2000 ))

$RANDOM是0到32767之间的数字。您希望端口在2000到65000之间。这有63001个可能的端口。如果我们坚持200033500之间的$RANDOM + 2000值,则覆盖31501个端口范围。如果我们抛硬币,然后有条件地在结果中加上31501,我们可以得到更多的端口,从3350165001。然后,如果我们只删除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专家,并希望将此转换为基于linux的bash脚本中的变量,请尝试以下方法:

VAR=$(shuf -i 200-700 -n 1)

这就得到了包含200到700的$VAR的范围。

你可以通过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

ruby也是一样:

echo $(ruby -e 'puts rand(20..65)') #=> 65 (inclusive ending)
echo $(ruby -e 'puts rand(20...65)') #=> 37 (exclusive ending)

这对我来说很管用:

export CUDA_VISIBLE_DEVICES=$((( RANDOM % 8 )))

如果你想让它从1开始而不是0,你可以加1。

在Bash中从10000000000 -999999999999范围内生成50个数字,并将它们保存到文件filename.csv

shuf -i 100000000000-999999999999 -n 50 -o filename.csv

生成范围为[$floor,$ceil)的随机数,不依赖:

$(((RANDOM % $(($ceil- $floor))) + $floor))

在2000到65000之间生成100个数字:

for i in $(seq 100); do echo $(((RANDOM % $((65000 - 2000))) + 2000));done