Bash 스크립트의 범위에서 난수


198

2000-65000쉘 스크립트에서 임의의 포트 번호를 생성해야합니다 . 문제는 $RANDOM15 비트 숫자이므로 멈췄습니다!

PORT=$(($RANDOM%63000+2001)) 크기 제한이 없다면 멋지게 작동합니다.

누구나 내가 어떻게 할 수 있는지에 대한 예를 가지고 있습니까? 어쩌면 무언가를 추출 /dev/urandom하여 범위 내에서 가져 오는 것입니까?

답변:


398
shuf -i 2000-65000 -n 1

즐겨!

편집 : 범위가 포함됩니다.


7
나는 shuf비교적 최근에 생각합니다 -지난 몇 년 동안 우분투 시스템에서 보았지만 현재 RHEL / CentOS는 아닙니다.
Cascabel

5
또한이 용도로 shuf는 괜찮을지 모르지만 실제로는 전체 입력을 순화 시킵니다. 난수를 매우 자주 생성하는 경우 나쁜 선택입니다.
Cascabel

3
@Jefromi : 내 시스템에서이 테스트를 사용 time for i in {1..1000}; do shuf -i 0-$end -n 1000 > /dev/null; done하고 비교 end=1end=65535만 반복 이상 4에 대한 초 차이 금액은 짧은 범위에 대한 25 %의 개선을 보여 주었다. 그리고 그건 많이 빠르게 OP의 배쉬 계산 만 번을 수행하는 것보다.
추후 공지가있을 때까지 일시 중지되었습니다.

9
@Dennis Williamson :로 테스트를 실행 -n 1해도 시간 차이는 무시할만한 수준으로 나타났습니다 end=4000000000. 알아두면 좋은 shuf점은 똑똑하지 않고 잘 작동 함 :-)
leedm777

6
나는 내 맥에 uf하지
않았다

79

Mac OS X 및 FreeBSD에서는 jot을 사용할 수도 있습니다.

jot -r 1  2000 65000

5
이 예에서는 jot구간의 최소값과 최대 값 (예 : 2000 및 65000)에 대한 불균형 분포가 있습니다. 즉, 최소 및 최대가 덜 자주 생성됩니다. 자세한 내용과 해결 방법 은 내 답변 을 참조하십시오 .
클린트 Pachl

jot대부분의 GNU / Linux 배포판에서도 사용 가능
Thor

43

bash 매뉴얼 페이지에 따르면 $RANDOM0에서 32767 사이에 배포됩니다. 즉, 부호없는 15 비트 값입니다. $RANDOM균일하게 분포되어 있다고 가정하면 다음과 같이 균일하게 분포 된 부호없는 30 비트 정수를 만들 수 있습니다.

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

범위가 2의 거듭 제곱이 아니기 때문에 간단한 모듈로 연산은 거의 균일 한 분포를 제공하지만 30 비트 입력 범위와 16 비트 미만의 출력 범위를 가지고 있습니다. 이것은 실제로 충분히 가까워 야합니다.

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

1
변수 $RANDOM가 모든 쉘에서 항상 사용 가능한 것은 아닙니다. 또 다른 솔루션을 찾고
루카스 Liesis

내가 이것을 올바르게 이해한다면, 당신은 1,000,000,000의 범위에서 32,000의 숫자를 퍼 뜨리고 있습니다. 그러나 2 ^ 15의 배수에만 적용됩니다. 1에서 2 ^ 30 사이의 모든 숫자를 균등하게 채우지 않고 2 ^ 15로 건너 뛰기 때문에 균일 한 분포입니다.
동 형사상

@isomorphismes 코드는 $RANDOM두 번 참조 합니다. 를 지원 $RANDOM하는 쉘에서는 참조 될 때마다 새 값이 생성됩니다. 따라서이 코드는 비트 0에서 14까지 하나의 $RANDOM값으로 채우고 비트 15에서 29까지는 다른 값으로 채 웁니다. $RANDOM균일하고 독립적 이라고 가정하면 아무것도 건너 뛰지 않고 0에서 2 ** 30-1까지의 모든 값을 포괄합니다.
Jesin

41

여기에 파이썬이 있습니다

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

하나는 awk

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

6
이 사람은 나에게서 공감대를 얻는다. 다양한 시스템에 대한 bash 스크립트를 작성하고 awk가 아마도 작업에 가장 풍부한 도구라고 생각합니다. Mac OS X 및 CentOS에서 문제없이 작업했으며 데비안 시스템과 다른 일반적인 * nix 시스템에서도 작동한다는 것을 알고 있습니다.
John Hunt

6
그러나 awk의 무작위 시드는 초당 1 회만 새로 고침되므로 a) 모든 비용을 피하거나 b) 시드를 다시 초기화 할 수 있습니다.
존 헌트

+1이 편집없이 유일한 POSIX 가능성 것으로 보인다 이유는 RANDOM, POSIX에 의해 보장되지 않습니다
치로 틸리郝海东冠状病六四事件法轮功

-S옵션을 사용하면 결과가 나타납니다 ImportError: No module named random. 내가 제거하면 작동합니다. 고스트 독의 의도가 무엇인지 확실하지 않습니다.
크리스 존슨

1
python -S -c "import random; print random.randrange(2000,63000)"잘 작동하는 것 같습니다. 그러나 1과 2 사이의 난수를 얻으려고 할 때 항상 1을 얻는 것 같습니다 ... 생각?
Hubert Léveillé Gauvin

17

가장 단순한 일반적인 방법은 펄 원 라이너입니다.

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

두 바이트를 읽고 부호없는 int로 인쇄합니다. 당신은 여전히 ​​클리핑을해야합니다.


나는이 기술을 사용하여 지금은 숫자가 생성되지 않고 단순히 공백이 있음을 알았습니다.
PdC

펄이 설치되어 있어야합니다. 나는 모든 리눅스 머신이 아니라면 대부분의 스크립트 awk에서 다른 답변의 버전을 고수해야한다
Lukas Liesis

난수를 추가하면 낮거나 높은 비용으로 중간 결과를 선호합니다. 균일하지 않습니다.
동 형사상

@isomorphismes 예, 문자 그대로 두 개의 난수를 추가하는 경우 가능합니다. 그러나 여기서 두 번째 표현을 언급한다고 가정하면, 그것이하는 것이 아닙니다. [0,32767]의 난수와 다음 비트 (예 : 0 또는 32768)에 대한 독립적 인 임의의 선택입니다. 균일합니다. (재 롤링하여 범위를 클립해야하기 때문에 원래 질문에는 이상적이지 않습니다.)
Cascabel

7

bash 전문가가 아니며 Linux 기반 bash 스크립트에서 변수로 가져 오려면 다음을 시도하십시오.

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

200에서 700까지의 범위를 $VAR포함합니다.


5

여기 또 다른 것이 있습니다. 나는 그것이 거의 무엇이든 작동 할 것이라고 생각했지만 직장의 centos 상자에서 sort의 임의 옵션을 사용할 수 없습니다.

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

3
sort -ROS X에서도 사용할 수 없습니다.
Lri

5

$RANDOM0과 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


5

루비와 동일 :

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

3

Bash 문서에 따르면$RANDOM 참조 될 때마다 0에서 32767 사이의 임의의 숫자가 반환됩니다. 두 개의 연속 참조를 합하면 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

p/q모든 63001 가능성이 주어진 확률을 얻기 위해 얼마나 많은 반복이 필요한지 결정하기 위해 아래 표현식을 사용할 수 있다고 생각합니다. 예를 들어, 여기의 1/2보다 큰 확률의 계산이고 , 그리고 여기에 9/10보다 큰 대 .

표현


1
네가 틀렸어. $RANDOM정수 . 당신의 "속임수"에는 결코 달성 할 수없는 많은 가치들이 있습니다. -1.
gniourf_gniourf

2
"정수"라는 것이 무엇인지 잘 모르겠지만 알고리즘이 잘못되었습니다. 제한된 범위에서 임의의 값을 곱해도 범위가 증가 하지 않습니다 . 우리는 $RANDOM대신 두 개의 액세스를 합산해야하며 $RANDOM모든 액세스에서 변경되어야하기 때문에 두 개의 곱셈으로 리팩토링하지 마십시오 . 합계 버전으로 답변을 업데이트했습니다.

6
그렇게하면 0에서 65534 사이의 임의의 숫자 RANDOM+RANDOM균일하게 분포 되지 않습니다.
gniourf_gniourf

3
다시 말해, 모든 합계가 똑같이 발생하는 것은 아닙니다. 사실, 그것과는 거리가 멀다. 그래프를 보면 피라미드이다! 이것이 내가 위의 공식에서 예상되는 것보다 상당히 큰 계산 시간을 얻은 이유라고 생각합니다. 모듈로 연산에도 문제가 있습니다. 63001에서 (32767 + 32767)의 합은 나머지 포트와 비교하여 처음 2534 포트에서 발생할 확률을 두 배로 늘립니다. 나는 대안을 생각하고 있지만 새로운 답변으로 처음부터 시작하는 것이 낫다고 생각하므로 삭제를 위해 투표하고 있습니다.

4
육면체 주사위 2 개를 굴리는 것과 같습니다. 통계적으로 그것은 "2"또는 "12"를 굴릴 확률이 낮고 중간에 "7"을 얻을 확률이 높은 종 곡선을 제공합니다.
오우거 시편


2

PORT=$(($RANDOM%63000+2001)) 당신이 원하는 것에 가깝습니다.

PORT=$(($RANDOM$RANDOM$RANDOM%63000+2001))당신을 괴롭히는 크기 제한을 극복합니다. bash는 숫자 변수와 문자열 변수를 구별하지 않기 때문에 완벽하게 작동합니다. "숫자" $RANDOM는 문자열처럼 연결 한 다음 계산에서 숫자로 사용할 수 있습니다. 놀랄 만한!


1
당신이 무슨 말을하는지 봅니다. 분포가 다를 것이라는 데 동의하지만 어쨌든 실제 임의성을 얻을 수는 없습니다. 보다 균일 한 배포를 위해 때때로 $ RANDOM, 때로는 $ RANDOM $ RANDOM 및 때로는 $ RANDOM $ RANDOM $ RANDOM을 사용하는 것이 좋습니다. 내가 알 수있는 한 더 많은 $ RANDOM은 더 높은 포트 번호를 선호합니다.
Wastrel

(잘못된 숫자 값을 사용하여 주석을 편집하기에는 너무 늦었으므로 원래 주석을 삭제했습니다.) 권리. x=$(( $n%63000 )대략 비슷합니다 x=$(( $n % 65535 )); if [ $x -gt 63000 ]; then x=63000.
chepner

나는 수학을 비판하지 않을 것입니다. 나는 그것을 단순히 받아 들였다. 이것은 내가 의미 한 바이다 : num = ($ RANDOM $ RANDOM $ RANDOM $ RANDOM $ RANDOM $ RANDOM); pick = $ (($ RANDOM % 3)); PORT = $ (($ {num [$ pick]} % 63000 + 2001)) --- 많은 문제가있는 것 같습니다 ...
Wastrel

1

당신은 난수를 통해 얻을 수 있습니다 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

이것이 일반적으로 난수를 생성하는 방법입니다. 그런 다음 사용하는 포트 번호의 변수로 "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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.