web-dev-qa-db-ja.com

Bashスクリプトの範囲からの乱数

シェルスクリプトから2000-65000の間にランダムなポート番号を生成する必要があります。問題は、$RANDOMが15ビットの数値であるため、立ち往生しています!

PORT=$(($RANDOM%63000+2001))は、サイズ制限がなければうまく動作します。

/dev/urandomから何かを抽出して範囲内で取得することで、これを行う方法の例はありますか?

165
Jason Gooner
shuf -i 2000-65000 -n 1

楽しい!

編集:範囲は包括的です。

337
leedm777

Mac OS XおよびFreeBSDでは、jotも使用できます。

jot -r 1  2000 65000
75
errno

Bashのマニュアルページによると、$RANDOMは0から32767の間で配布されます。つまり、符号なしの15ビット値です。 $RANDOMが均一に分散されていると仮定すると、次のように均一に分散された符号なし30ビット整数を作成できます。

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

範囲は2の累乗ではないため、単純なモジュロ演算は ほぼ あなたは一様な分布を与えますが、あなたの場合のように、30ビットの入力範囲と16ビット未満の出力範囲で、これは本当に十分に近いはずです:

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

そして、これはPythonのものです

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

そしてawkのあるもの

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

頭に浮かぶ最も簡単な一般的な方法は、Perlのワンライナーです。

Perl -e 'print int(Rand(65000-2000)) + 2000'

常に2つの数字を使用できます。

PORT=$(($RANDOM + ($RANDOM % 2) * 32768))

あなたはまだあなたの範囲にクリップする必要があります。これは一般的なnビットの乱数法ではありませんが、あなたの場合には機能し、すべてbashの中にあります。

本当に可愛くて/ dev/urandomから読みたい場合、これを行うことができます:

od -A n -N 2 -t u2 /dev/urandom

これは2バイトを読み取り、それらを符号なしintとして出力します。クリッピングを行う必要があります。

14
Cascabel

$RANDOMは0〜32767の数値です。2000〜65000のポートが必要です。これらは63001の可能なポートです。 205の間の$RANDOM + 2000の値に固執する場合、31501ポートの範囲をカバーします。コインをフリップし、結果に31501を条件付きで追加すると、501から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
5
Renato Silva

もう1つあります。私はそれが何でも動作すると思ったが、ソートのランダムなオプションは仕事中の私のcentosボックスでは利用できない。

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

あなたはこれを行うことができます

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 をご覧ください。

5
New Exit

あなたがbashの専門家ではなく、これをLinuxベースのbashスクリプトの変数に入れたい場合は、これを試してください。

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

これにより、200から700の範囲が$VARになります。

5
Berto

Rubyと同じ:

echo $(Ruby -e 'puts Rand(20..65)') #=> 65 (inclusive ending)
echo $(Ruby -e 'puts Rand(20...65)') #=> 37 (exclusive ending)
4
Lev Lukomsky

Bashドキュメントによれば$RANDOMが参照されるたびに、0から32767の間の乱数が返されます。 2つの連続する参照を合計すると、0〜65534の値が得られます。これは、2000〜65000の乱数の63001の可能性の望ましい範囲をカバーします。

正確な範囲に調整するには、63001を法とする合計を使用します。これにより、0〜63000の値が得られます。これは、2000〜65000の乱数を提供するために2000ずつ増加するだけです。次のように要約されます。

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

3
anon

または、OS-Xでは次のように機能します。

$ gsort --random-sort
2
Chadwick Boggs

PORT=$(($RANDOM%63000+2001))はあなたが望むものに近いです。

PORT=$(($RANDOM$RANDOM$RANDOM%63000+2001))は、問題を引き起こすサイズ制限を回避します。 bashは数値変数と文字列変数を区別しないため、これは完全に機能します。 「数値」$RANDOMは、文字列のように連結して、計算で数値として使用できます。すごい!

2
Wastrel

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

0
zangw

これは私が通常乱数を生成する方法です。次に、使用するポート番号の変数として「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
0
Yokai