it-swarm.com.de

Zufallszahl aus einem Bereich in einem Bash-Skript

Ich muss eine zufällige Portnummer zwischen 2000-65000 Aus einem Shell-Skript generieren. Das Problem ist, dass $RANDOM Eine 15-Bit-Zahl ist, also stecke ich fest!

PORT=$(($RANDOM%63000+2001)) würde gut funktionieren, wenn es nicht die Größenbeschränkung gäbe.

Hat jemand ein Beispiel dafür, wie ich das tun kann, indem ich vielleicht etwas aus /dev/urandom Extrahiere und es in einen bestimmten Bereich bringe?

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

Genießen!

Bearbeiten: Der Bereich ist inklusive.

345
leedm777

Unter Mac OS X und FreeBSD können Sie auch jot verwenden:

jot -r 1  2000 65000
75
errno

Laut der Bash-Manpage ist $RANDOM ist zwischen 0 und 32767 verteilt; Dies ist ein vorzeichenloser 15-Bit-Wert. Angenommen $RANDOM ist gleichmäßig verteilt. Sie können eine gleichmäßig verteilte 30-Bit-Ganzzahl ohne Vorzeichen wie folgt erstellen:

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

Da Ihre Reichweite keine Potenz von 2 ist, wird nur eine einfache Modulo-Operation ausgeführt fast Sie erhalten eine gleichmäßige Verteilung, aber mit einem Eingangsbereich von 30 Bit und einem Ausgangsbereich von weniger als 16 Bit, wie Sie es in Ihrem Fall getan haben, sollte dies wirklich nah genug sein:

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

und hier ist einer mit Python

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

und einer mit awk

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

Der einfachste allgemeine Weg, der einem in den Sinn kommt, ist ein Perl-Einzeiler:

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

Sie könnten immer nur zwei Zahlen verwenden:

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

Sie müssen immer noch an Ihrem Bereich befestigen. Es ist keine allgemeine n-Bit-Zufallszahlmethode, aber es funktioniert in Ihrem Fall und alles ist in bash enthalten.

Wenn Sie wirklich süß sein und aus/dev/urandom lesen möchten, können Sie dies tun:

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

Das liest zwei Bytes und gibt sie als vorzeichenloses int aus. Sie müssen noch Ihren Ausschnitt machen.

14
Cascabel

$RANDOM ist eine Zahl zwischen 0 und 32767. Sie möchten einen Port zwischen 2000 und 65000. Dies sind 63001 mögliche Ports. Wenn wir uns an die Werte von $RANDOM + 2000 zwischen 20 und 5 decken wir einen Bereich von 31501 Ports ab. Wenn wir eine Münze werfen und dann 31501 zum Ergebnis addieren, können wir mehr Ports erhalten, von 501 bis 65001. Wenn wir dann nur 65001 fallen lassen, erhalten wir die genaue Abdeckung, die erforderlich ist, mit einer anscheinend einheitlichen Wahrscheinlichkeitsverteilung für alle Ports.

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
}

Testen

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

Hier ist ein anderes. Ich dachte, es würde auf fast allem funktionieren, aber die zufällige Sortieroption ist auf meiner Centos-Box bei der Arbeit nicht verfügbar.

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

Du kannst das

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

Wenn Sie weitere Informationen benötigen, lesen Sie Shell Script Random Number Generator .

5
New Exit

Wenn Sie kein Bash-Experte sind und dies in einem Linux-basierten Bash-Skript in eine Variable umwandeln möchten, versuchen Sie Folgendes:

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

Das bringt Ihnen den Bereich von 200 bis 700 in $VAR, einschließlich.

5
Berto

Gleiches gilt für Ruby:

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

In der Bash-Dokumentation steht dass jedes Mal, wenn auf $RANDOM Verwiesen wird, eine Zufallszahl zwischen 0 und 32767 zurückgegeben wird. Wenn wir zwei aufeinanderfolgende Referenzen addieren, erhalten wir Werte von 0 bis 65534, was den gewünschten Bereich von 63001 Möglichkeiten für eine Zufallszahl zwischen 2000 und 65000 abdeckt.

Um es auf den genauen Bereich einzustellen, verwenden wir das Summenmodul 63001, das uns einen Wert von 0 bis 63000 gibt. Dies erfordert wiederum nur ein Inkrement von 2000, um die gewünschte Zufallszahl zwischen 2000 und 65000 bereitzustellen. Dies kann sein wie folgt zusammengefasst:

port=$((((RANDOM + RANDOM) % 63001) + 2000))

Testen

# 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

Richtigkeit der Berechnung

Hier ist ein vollständiger Brute-Force-Test für die Richtigkeit der Berechnung. Dieses Programm versucht nur, alle 63001 verschiedenen Möglichkeiten zufällig unter Verwendung der zu testenden Berechnung zu generieren. Der Parameter --jobs Sollte die Ausführung beschleunigen, ist jedoch nicht deterministisch (die Anzahl der generierten Möglichkeiten kann unter 63001 liegen).

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

Ich glaube, wir können den folgenden Ausdruck verwenden, um zu bestimmen, wie viele Iterationen erforderlich sind, um eine bestimmte Wahrscheinlichkeit p/q Für die Generierung aller 63001-Möglichkeiten zu erhalten. Beispiel: hier ist die Berechnung für eine Wahrscheinlichkeit größer als 1/2 und hier für eine Wahrscheinlichkeit größer als 9/1 .

Expression

3
anon

Oder unter OS-X funktioniert folgendes für mich:

$ gsort --random-sort
2
Chadwick Boggs

PORT=$(($RANDOM%63000+2001)) ist in der Nähe von dem, was du willst, denke ich.

PORT=$(($RANDOM$RANDOM$RANDOM%63000+2001)) umgeht die Größenbeschränkung, die Sie stört. Da bash nicht zwischen einer Zahlenvariablen und einer Stringvariablen unterscheidet, funktioniert dies einwandfrei. Die Nummer" $RANDOM kann wie eine Zeichenfolge verkettet und dann als Zahl in einer Berechnung verwendet werden. Tolle!

2
Wastrel

Sie können die Zufallszahl durch urandom erhalten

head -200 /dev/urandom | cksum

Ausgabe:

3310670062 52870

So rufen Sie den einen Teil der obigen Nummer ab.

head -200 /dev/urandom | cksum | cut -f1 -d " "

Dann ist die Ausgabe

3310670062

Um ihre anforderung zu erfüllen,

head -200 /dev/urandom |cksum | cut -f1 -d " " | awk '{print $1%63000+2001}'

0
zangw

So generiere ich normalerweise Zufallszahlen. Dann verwende ich "NUM_1" als Variable für die von mir verwendete Portnummer. Hier ist ein kurzes Beispielskript.

#!/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