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

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

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


当前回答

如果你需要大于15位的范围,不要使用慢的不安全的过时的15位RANDOM,使用快速安全的32位SRANDOM。

SRANDOM自2021年bash 5.1推出以来可用。

Bash 5.1中值得注意的一个有趣的添加是新的SRANDOM变量。SRANDOM变量提供来自系统熵引擎的随机数据,不能被重新播种。特别是,SRANDOM变量提供了一个32位随机数,它依赖于getrandom/getentropy——如果必要的话,还可以回退到/dev/urandom或arc4random,甚至在此之后的另一个回退。”

来源:https://www.phoronix.com/news/gnu - bash - 5.1

看看bash中的RANDOM和SRANDOM有什么不同:

Bash中RANDOM和SRANDOM的区别

请随意改进这个答案。

其他回答

Bash文档说,每次引用$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的概率计算。

你可以这样做

cat /dev/urandom|od -N2 -An -i|awk -v f=2000 -v r=65000 '{printf "%i\n", f + r * $1 / 65536}'

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

这是一个用Python写的

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

还有一个是awk

awk 'BEGIN{srand();print int(rand()*(63000-2000))+2000 }'
shuf -i 2000-65000 -n 1

享受吧!

编辑:范围是包括在内的。

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

 seq 2000 65000 | sort -R | head -n 1