Bash脚本中范围内的随机数

时间:2010-03-31 20:21:04

标签: bash shell scripting

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

如果不是因为尺寸限制,

PORT=$(($RANDOM%63000+2001))会很好地工作。

有没有人有一个如何做到这一点的例子,可能是从/dev/urandom中提取并将其放入范围内?

16 个答案:

答案 0 :(得分:335)

shuf -i 2000-65000 -n 1

享受!

编辑:范围包含在内。

答案 1 :(得分:74)

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

jot -r 1  2000 65000

答案 2 :(得分:38)

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

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

由于你的范围不是2的幂,一个简单的模运算只会几乎给你一个均匀的分布,但是输入范围是30位且小于16位输出范围,就像你的情况一样,这应该足够接近:

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

答案 3 :(得分:36)

这是Python的一个

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

和一个用awk

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

答案 4 :(得分:14)

想到的最简单的一般方法是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;你仍然需要剪裁。

答案 5 :(得分:5)

这是另一个。我认为它几乎可以解决任何问题,但是我的centos盒子里没有sort的随机选项。

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

答案 6 :(得分:5)

你可以这样做

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

如果您需要更多详细信息,请参阅Shell Script Random Number Generator

答案 7 :(得分:5)

$RANDOM是0到32767之间的数字。您需要一个介于2000和65000之间的端口。这些是63001个可能的端口。如果我们坚持 2000 33500 之间的$RANDOM + 2000值,我们会覆盖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

答案 8 :(得分:5)

如果您不是bash专家,并希望将其转换为基于Linux的bash脚本中的变量,请尝试以下操作:

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

这将使您获得$VAR范围内的200到700的范围。

答案 9 :(得分:3)

Bash documentation says每次引用$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需要多少次迭代,我相信我们可以使用下面的表达式。例如,here is the calculation for a probability greater than 1/2here for greater than 9/10

Expression

答案 10 :(得分:3)

与ruby相同:

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

答案 11 :(得分:2)

或者在OS-X上,以下内容适用于我:

$ gsort --random-sort

答案 12 :(得分:2)

PORT=$(($RANDOM%63000+2001))接近您的想法。

PORT=$(($RANDOM$RANDOM$RANDOM%63000+2001))克服了麻烦你的大小限制。由于bash没有对数字变量和字符串变量进行区分,所以这非常有效。 “数字”$RANDOM可以像字符串一样连接,然后在计算中用作数字。惊人!

答案 13 :(得分:0)

您可以通过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}'

答案 14 :(得分:0)

这就是我通常生成随机数的方法。然后我使用“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

答案 15 :(得分:0)

这对我有用:

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

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