Bagaimana cara menghasilkan angka acak di Bash?

236

Bagaimana cara menghasilkan angka acak dalam rentang dalam Bash?

Woaka
sumber
21
Seberapa acak itu perlu?
bdonlan

Jawaban:

283

Gunakan $RANDOM. Ini sering berguna dalam kombinasi dengan aritmatika shell sederhana. Misalnya, untuk menghasilkan angka acak antara 1 dan 10 (inklusif):

$ echo $((1 + RANDOM % 10))
3

Generator yang sebenarnya ada variables.c, fungsinya brand(). Versi yang lebih lama adalah generator linier sederhana. Versi 4.0 bashmenggunakan generator dengan kutipan ke kertas 1985, yang mungkin berarti itu adalah sumber angka pseudorandom yang layak. Saya tidak akan menggunakannya untuk simulasi (dan tentu saja tidak untuk crypto), tetapi mungkin cukup untuk tugas-tugas scripting dasar.

Jika Anda melakukan sesuatu yang memerlukan angka acak serius yang dapat Anda gunakan /dev/randomatau /dev/urandomjika tersedia:

$ dd if=/dev/urandom count=4 bs=1 | od -t d
Nelson
sumber
21
Hati-hati di sini. Meskipun hal ini baik-baik saja dalam keadaan darurat, melakukan aritmatika pada angka acak dapat secara dramatis memengaruhi keacakan hasil Anda. dalam kasus $RANDOM % 108 dan 9 secara terukur (meskipun secara marginal) kurang mungkin dari 0-7, bahkan jika $RANDOMmerupakan sumber data acak yang kuat.
dimo414
3
@ dimo414 Saya ingin tahu "secara marginal", apakah Anda memiliki sumber di mana saya dapat mengetahui lebih banyak tentang ini?
PascalVKooten
58
Dengan memodulasi input acak Anda, Anda " merpati " hasilnya. Karena $RANDOMrentang adalah 0-32767angka 0- 7petakan ke 3277berbagai input yang mungkin, tetapi 8dan 9hanya dapat diproduksi 3276dengan cara yang berbeda (karena 32768dan 32769tidak mungkin). Ini adalah masalah kecil untuk peretasan cepat, tetapi artinya hasilnya tidak acak acak. Pustaka acak, seperti Java Random, menawarkan fungsi untuk mengembalikan angka acak seragam dalam kisaran yang diberikan dengan benar, daripada hanya memodulasi angka yang tidak dapat dibagi.
dimo414
1
@ JFSebastian sangat benar - masalah dengan modulo adalah dapat merusak keseragaman setiap Ping, bukan hanya PRNGs buruk, tapi terima kasih untuk memanggil ini.
dimo414
14
Hanya untuk konteks, pigeonholing dasar untuk% 10 berarti 8 dan 9 sekitar 0,03% lebih kecil kemungkinannya terjadi daripada 0–7. Jika skrip shell Anda memerlukan angka acak seragam yang lebih akurat daripada itu, maka tentu saja gunakan mekanisme yang lebih kompleks dan tepat.
Nelson
71

Silakan lihat $RANDOM:

$RANDOM adalah fungsi Bash internal (bukan konstanta) yang mengembalikan integer pseudorandom dalam rentang 0 - 32767. Fungsi ini seharusnya tidak digunakan untuk menghasilkan kunci enkripsi.

Andrew Hare
sumber
1
Apakah 32767ada arti khusus?
Jin Kwon
14
@ JinKwon 32767adalah 2^16 / 2 - 1batas atas untuk integer 16 bit yang telah ditandatangani.
Jeffrey Martinez
@ JinKwon dapatkah Anda menjelaskan mengapa Anda tidak mengatakannya 2^15 - 1? Ini setara, jadi saya hanya ingin tahu apakah ada konteks yang hilang?
Brett Holman
11
@ BrettHolman Saya pikir dia sedang mencoba untuk menunjukkan "ditandatangani" bagian dari integer 16 bit yang ditandatangani. 2 ^ 16 nilai, dibagi dua untuk postive & int negatif.
cody
Jawaban ini tidak menjawab pertanyaan.
bocor
48

Anda juga dapat menggunakan shuf (tersedia di coreutils).

shuf -i 1-100000 -n 1
knipwim
sumber
Bagaimana Anda melewati vars sebagai ujung jangkauan? Saya mendapat ini:shuf -i 1-10 -n 1: syntax error in expression (error token is "1-10 -n 1")
dat tutbrus
1
Tambahkan $varbukan rentang akhir, seperti ini:var=100 && shuf -i 1-${var} -n 1
knipwim
2
Saya lebih suka opsi ini karena mudah untuk menghasilkan angka acak N dengan -n. Misalnya Hasilkan 5 angka acak antara 1 dan 100 :shuf -i 1-100 -n 5
aerijman
Sejauh yang saya mengerti angkanya tidak acak. Jika Anda menentukan, shuf -i 1-10 -n 10Anda akan mendapatkan semua angka dari 1 hingga 10 persis satu. Jika Anda menentukan, -n 15Anda masih akan mendapatkan hanya 10 angka itu sekali saja. Itu hanya mengocok, tidak menghasilkan angka acak.
radlan
Untuk mendapatkan nomor acak dengan penggantian: -r
Geoffrey Anderson
36

Coba ini dari shell Anda:

$ od -A n -t d -N 1 /dev/urandom

Di sini, -t dmenentukan bahwa format output harus ditandatangani desimal; -N 1mengatakan untuk membaca satu byte dari /dev/urandom.

Barun
sumber
2
Pertanyaannya menanyakan angka dalam kisaran.
JSycamore
9
Anda dapat menghapus spasi:od -A n -t d -N 1 /dev/urandom |tr -d ' '
Robert
23

Anda juga bisa mendapatkan nomor acak dari awk

awk 'BEGIN {
   # seed
   srand()
   for (i=1;i<=1000;i++){
     print int(1 + rand() * 100)
   }
}'
ghostdog74
sumber
1
+1 Anda tahu, pada awalnya saya berpikir mengapa Anda ingin melakukannya seperti ini, tetapi sebenarnya saya sangat menyukainya.
zelanix
1
Terima kasih telah memberikan solusi yang mencakup penyemaian. Saya tidak dapat menemukannya di mana pun!
so.very.tired
2
+1 untuk penyemaian. Mungkin perlu disebutkan bahwa srand()seed adalah waktu CPU saat ini. Jika Anda perlu menentukan seed tertentu, sehingga RNG dapat diduplikasi, gunakan di srand(x)mana xseed tersebut. Juga, dikutip dari manual fungsi numerik GNU awk, "implementasi awk yang berbeda menggunakan generator nomor acak yang berbeda secara internal." Hasilnya adalah jika Anda tertarik untuk menghasilkan distribusi statistik, Anda harus mengharapkan sedikit variasi dari satu runtime ke yang berikutnya pada platform yang berbeda (semua berjalan awkatau gawk).
Cbhihe
18

Ada $ ACAK. Saya tidak tahu persis bagaimana cara kerjanya. Tapi itu berhasil. Untuk pengujian, Anda dapat melakukan:

echo $RANDOM
Antoine Claval
sumber
16

Saya suka trik ini:

echo ${RANDOM:0:1} # random number between 1 and 9
echo ${RANDOM:0:2} # random number between 1 and 99

...

lemah
sumber
7
$ RANDOM berada dalam kisaran 0 - 32767. Anda akan berakhir dengan lebih banyak angka yang dimulai dengan 1, 2 atau 3, daripada Anda akan 4-9. Jika Anda baik-baik saja dengan distribusi yang tidak seimbang, ini akan berfungsi dengan baik.
jbo5112
2
@ jbo5112 Anda benar, bagaimana dengan menampilkan digit terakhir? echo $ {RANDOM: 0-1} untuk satu digit, $ {RANDOM: 0-2} untuk dua digit ...?
fraff
4
Jika Anda menggunakan digit terakhir, angka itu akan lebih baik, tetapi itu termasuk 0 dan 00. Pada satu digit, 0-7 akan terjadi 0,03% lebih sering daripada 8-9. Pada 2 digit, 0-67 akan terjadi 0,3% lebih sering daripada 68-99. Jika Anda membutuhkan distribusi angka acak yang bagus, semoga Anda tidak menggunakan bash. Dengan yang asli: ${RANDOM:0:1}memiliki peluang 67,8% untuk memberi Anda 1 atau 2, ${RANDOM:0:2}hanya memiliki peluang 0,03% untuk memberi Anda satu digit angka (harus 1%), dan keduanya memiliki peluang 0,003% untuk memberi Anda 0 Masih ada kasus penggunaan di mana ini baik-baik saja (mis. Input tidak konsisten).
jbo5112
12

Angka acak antara 0 dan 9 inklusif.

echo $((RANDOM%10))
David Newcomb
sumber
2
Buruk saya, tidak membaca halaman manual dengan benar. $RANDOMhanya berlaku dari 0 hingga 32767. Seharusnya dikatakan "Angka acak sebagian besar antara 1 dan 3, dengan beberapa sayap";)
David Newcomb
1
Apa? Itu masih akan antara 0 dan 9, meskipun 8 dan 9 akan memiliki sedikit kemungkinan lebih kecil terjadi dari 0 sampai 7, seperti yang disebutkan dalam jawaban lain.
kini
6

Jika Anda menggunakan sistem linux, Anda bisa mendapatkan nomor acak dari / dev / random atau / dev / urandom. Hati-hati / dev / random akan memblokir jika tidak ada angka acak yang cukup. Jika Anda membutuhkan kecepatan lebih dari keacakan gunakan / dev / urandom.

"File" ini akan diisi dengan angka acak yang dihasilkan oleh sistem operasi. Tergantung pada implementasi / dev / random pada sistem Anda jika Anda mendapatkan nomor acak benar atau pseudo. Angka acak yang sebenarnya dihasilkan dengan noise bentuk bantuan yang dikumpulkan dari driver perangkat seperti mouse, hard drive, jaringan.

Anda bisa mendapatkan nomor acak dari file dengan dd

Janusz
sumber
5

Saya telah mengambil beberapa dari ide-ide ini dan membuat fungsi yang seharusnya bekerja dengan cepat jika banyak angka acak diperlukan.

panggilan oditu mahal jika Anda membutuhkan banyak nomor acak. Sebagai gantinya saya menyebutnya sekali dan menyimpan 1024 nomor acak dari / dev / urandom. Ketika randdipanggil, nomor acak terakhir dikembalikan dan diskalakan. Itu kemudian dihapus dari cache. Ketika cache kosong, 1024 angka acak lainnya dibaca.

Contoh:

rand 10; echo $RET

Mengembalikan angka acak dalam RET antara 0 dan 9 inklusif.

declare -ia RANDCACHE
declare -i RET RAWRAND=$(( (1<<32)-1 ))

function rand(){  # pick a random number from 0 to N-1. Max N is 2^32
  local -i N=$1
  [[ ${#RANDCACHE[*]} -eq 0 ]] && { RANDCACHE=( $(od -An -tu4 -N1024 /dev/urandom) ); }  # refill cache
  RET=$(( (RANDCACHE[-1]*N+1)/RAWRAND ))  # pull last random number and scale
  unset RANDCACHE[${#RANDCACHE[*]}-1]     # pop read random number
};

# test by generating a lot of random numbers, then effectively place them in bins and count how many are in each bin.

declare -i c; declare -ia BIN

for (( c=0; c<100000; c++ )); do
  rand 10
  BIN[RET]+=1  # add to bin to check distribution
done

for (( c=0; c<10; c++ )); do
  printf "%d %d\n" $c ${BIN[c]} 
done

UPDATE: Itu tidak bekerja dengan baik untuk semua N. Ini juga membuang bit acak jika digunakan dengan N. kecil. Memperhatikan bahwa (dalam hal ini) angka acak 32 bit memiliki cukup entropi untuk 9 angka acak antara 0 dan 9 (10 * 9 = 1.000.000.000 <= 2 * 32) kita dapat mengekstraksi beberapa angka acak dari setiap 32 nilai sumber acak.

#!/bin/bash

declare -ia RCACHE

declare -i RET             # return value
declare -i ENT=2           # keep track of unused entropy as 2^(entropy)
declare -i RND=RANDOM%ENT  # a store for unused entropy - start with 1 bit

declare -i BYTES=4         # size of unsigned random bytes returned by od
declare -i BITS=8*BYTES    # size of random data returned by od in bits
declare -i CACHE=16        # number of random numbers to cache
declare -i MAX=2**BITS     # quantum of entropy per cached random number
declare -i c

function rand(){  # pick a random number from 0 to 2^BITS-1
  [[ ${#RCACHE[*]} -eq 0 ]] && { RCACHE=( $(od -An -tu$BYTES -N$CACHE /dev/urandom) ); }  # refill cache - could use /dev/random if CACHE is small
  RET=${RCACHE[-1]}              # pull last random number and scale
  unset RCACHE[${#RCACHE[*]}-1]  # pop read random number
};

function randBetween(){
  local -i N=$1
  [[ ENT -lt N ]] && {  # not enough entropy to supply ln(N)/ln(2) bits
    rand; RND=RET       # get more random bits
    ENT=MAX             # reset entropy
  }
  RET=RND%N  # random number to return
  RND=RND/N  # remaining randomness
  ENT=ENT/N  # remaining entropy
};

declare -ia BIN

for (( c=0; c<100000; c++ )); do
  randBetween 10
  BIN[RET]+=1
done

for c in ${BIN[*]}; do
  echo $c
done
philcolbourn
sumber
Saya mencoba ini - butuh 10 detik cpu 100% dan kemudian dicetak 10 angka yang tidak terlihat acak sama sekali.
Carlo Wood
Aku ingat sekarang. Kode ini menghasilkan 100.000 angka acak. Ini menempatkan masing-masing dalam 'bin' untuk melihat seberapa acak itu. Ada 10 nampan. Angka-angka ini harus sama jika setiap angka acak antara 0 dan 9 memiliki kemungkinan yang sama. Jika Anda ingin mencetak setiap angka, gema $ RET setelah randBetween 10.
philcolbourn
od -An -tu4 -N40 /dev/urandomakan menghasilkan 10 bilangan bulat 32 bit yang tidak ditandatangani dan dipisahkan dengan spasi. Anda bisa menyimpannya dalam sebuah array dan menggunakannya sesudahnya. kode Anda tampaknya berlebihan.
Ali
@ Ali, OP tidak menentukan bahwa mereka ingin 32 bit atau nomor acak berukuran lain. Saya dan beberapa orang lainnya menafsirkan pertanyaan ini sebagai memberikan nomor acak dalam suatu rentang. Fungsi rand saya mencapai tujuan ini dan juga mengurangi hilangnya entropi yang, jika habis, menyebabkan program untuk diblokir. od on / dev / urandom mengembalikan hanya 2 ^ N angka acak bit dan OP kemudian perlu menyimpan beberapa nilai ke dalam array, secara berurutan mengekstraksi mereka dari array ini, dan mengisi ulang array ini. Mungkin Anda bisa mengkodekan ini sebagai jawaban dan menangani rentang angka acak lainnya?
philcolbourn
@ philcolbourn, Anda benar tentang OP tidak menentukan apa nomor acak yang dia inginkan dan itu merindukan perhatian saya. Tetapi dia hanya bertanya: "Bagaimana cara menghasilkan angka acak dalam bash?" Maksud saya adalah dia hanya meminta satu nomor acak. Meskipun kritik ini berlaku untuk komentar saya sebelumnya (menghasilkan 10 angka acak) juga.
Ali
5

Membaca dari file khusus karakter / dev / random atau / dev / urandom adalah cara yang harus dilakukan.

Perangkat ini mengembalikan angka acak ketika dibaca dan dirancang untuk membantu perangkat lunak aplikasi memilih kunci aman untuk enkripsi. Angka acak tersebut diekstraksi dari kumpulan entropi yang disumbangkan oleh berbagai peristiwa acak. {LDD3, Jonathan Corbet, Alessandro Rubini, dan Greg Kroah-Hartman]

Kedua file ini adalah antarmuka untuk pengacakan kernel, khususnya

void get_random_bytes_arch(void* buf, int nbytes)

yang menarik byte benar-benar acak dari perangkat keras jika fungsi tersebut dilakukan dengan perangkat keras (biasanya), atau menarik dari kumpulan entropi (terdiri dari pengaturan waktu antara peristiwa seperti mouse dan keyboard interupsi dan interupsi lainnya yang terdaftar pada SA_SAMPLE_RANDOM).

dd if=/dev/urandom count=4 bs=1 | od -t d

Ini berfungsi, tetapi menulis keluaran yang tidak dibutuhkan dari ddke stdout. Perintah di bawah ini hanya memberikan integer yang saya butuhkan. Saya bahkan bisa mendapatkan jumlah bit acak yang ditentukan sesuai kebutuhan dengan penyesuaian bitmask yang diberikan untuk ekspansi aritmatika:

me@mymachine:~/$ x=$(head -c 1 /dev/urandom > tmp && hexdump 
                         -d tmp | head -n 1 | cut -c13-15) && echo $(( 10#$x & 127 ))
4pie0
sumber
3

Bagaimana dengan:

perl -e 'print int rand 10, "\n"; '
kh
sumber
1
Untuk nomor acak yang aman secara kriptografis, Anda perlu membaca dari / dev / urandom atau menggunakan pustaka Crypt :: Random.
kh
3

Mungkin saya sedikit terlambat, tetapi bagaimana dengan menggunakan jotuntuk menghasilkan angka acak dalam rentang di Bash?

jot -r -p 3 1 0 1

Ini menghasilkan angka acak ( -r) dengan 3 tempat desimal presisi ( -p). Dalam kasus khusus ini, Anda akan mendapatkan satu angka antara 0 dan 1 ( 1 0 1). Anda juga dapat mencetak data berurutan. Sumber nomor acak, menurut manual, adalah:

Angka acak diperoleh melalui arc4random (3) saat tidak ada benih yang ditentukan, dan melalui acak (3) saat benih diberikan.

Vinicius Placco
sumber
1
Harus diinstal: sudo apt install athena-jot
xerostomus
3

Berdasarkan jawaban hebat dari @Nelson, @Barun dan @Robert, berikut adalah skrip Bash yang menghasilkan angka acak.

  • Dapat menghasilkan berapa digit yang Anda inginkan.
  • setiap digit secara terpisah dihasilkan dengan /dev/urandomyang jauh lebih baik daripada bawaan Bash$RANDOM
#!/usr/bin/env bash

digits=10

rand=$(od -A n -t d -N 2 /dev/urandom |tr -d ' ')
num=$((rand % 10))
while [ ${#num} -lt $digits ]; do
  rand=$(od -A n -t d -N 1 /dev/urandom |tr -d ' ')
  num="${num}$((rand % 10))"
done
echo $num
Euforia
sumber
Persis seperti yang saya kejar.
Prometheus
2

Hasilkan angka acak dalam kisaran 0 hingga n (bertanda integer 16-bit). Hasil ditetapkan dalam variabel $ RAND. Sebagai contoh:

#!/bin/bash

random()
{
    local range=${1:-1}

    RAND=`od -t uI -N 4 /dev/urandom | awk '{print $2}'`
    let "RAND=$RAND%($range+1)"
}

n=10
while [ $(( n -=1 )) -ge "0" ]; do
    random 500
    echo "$RAND"
done
Pavlo Bashynskyi
sumber
1

Percabangan acak suatu program atau ya / tidak; 1/0; output benar / salah:

if [ $RANDOM -gt 16383  ]; then              # 16383 = 32767/2 
    echo var=true/1/yes/go_hither
else 
    echo var=false/0/no/go_thither
fi

dari jika Anda malas mengingat 16383:

if (( RANDOM % 2 )); then 
    echo "yes"
else 
    echo "no"
fi
xerostomus
sumber