HOWTO · Bash

Bash で乱数を生成する

このチュートリアルでは、Bash での乱数の生成について説明します。

このページの内容

Bash スクリプトでは、サンプリング、テストデータ、簡単なゲームなどに使う乱数の整数や疑似乱数の浮動小数点値を生成できます。適切な方法は、必要な範囲、利用できるコマンド、予測困難性が重要かどうかによって異なります。値は実行のたびに変わるため、以下では固定の出力ではなくコマンドを示します。パスワード、トークンなど、セキュリティ上重要な値の生成にこれらの例を使わないでください。

範囲を指定した整数に $RANDOM を使う

Bash の $RANDOM は、0 から 32767 までの整数に展開されます。両端を含む範囲に変換するには、範囲の幅で割った余りを求め、下限を加えます。次の例を実行してください。

このセクションのコードと表示されるラベルや出力は英語のままです。

min=1
max=100
span=$((max - min + 1))
printf '%s\n' "$((RANDOM % span + min))"

両端を含めるため、幅は max - min + 1 です。結果は1から100までの値になります。剰余による変換は簡潔ですが、偏りが生じることがあります。幅が32768を割り切れない場合、元の値のうち一部は、ほかの値より多くの値に対応します。$RANDOM は気軽な抽選や簡単なスクリプトには適していますが、暗号用途には適しません。

ランダムな整数の選択に shuf を使う

GNU coreutils がインストールされているシステムでは、shuf を使って両端を含む整数範囲から値を選べます。-i オプションで範囲を指定し、-n で選ぶ行数を指定します。0から100までの数を1つ選ぶには、次のようにします。

このセクションのコードと表示されるラベルや出力は英語のままです。

shuf -i 0-100 -n 1

この範囲から重複しない数を5つ選ぶには、shuf -i 0-100 -n 5 を使います。重複なしで選ぶため、-n は範囲内の整数の個数を超えられません。コマンドが利用でき、異なる値を選びたい場合にこの方法を使ってください。GNU coreutils が標準ではないシステムでは、利用できるか確認してください。

/dev/urandom からバイトを読み取る

Linux では、/dev/urandom がオペレーティングシステムの乱数生成器からバイト列を提供します。od を使うと、1バイトを符号なし10進数として表示できます。

このセクションのコードと表示されるラベルや出力は英語のままです。

od -An -N1 -tu1 /dev/urandom

ここで -A n はアドレス列を省略し、-N1 は1バイトを読み取り、-tu1 は符号なしの1バイト整数として整形します。そのため、表示される値は0から255までです。1バイトだけでは、より大きな範囲の値を作る一般的な方法にはなりません。単純に剰余を取ると偏りが生じることがあり、元データの品質が高くても、不適切な変換が正しくなるわけではありません。秘密情報には、シェルの断片的な処理で値を組み立てるのではなく、オペレーティングシステムが提供する暗号用インターフェースを使ってください。

awk で浮動小数点値を生成する

rand() 関数は Bash ではなく awk に属します。0以上1未満の疑似乱数の浮動小数点値を返します。次のコマンドは生成器にシードを設定し、値を1つ表示します。

このセクションのコードと表示されるラベルや出力は英語のままです。

awk 'BEGIN { srand(); print rand() }'

結果に正の幅を掛けて下限を加えると、対応する浮動小数点範囲に変換できます。値の並びと書式は、awk の実装とシードによって異なります。簡単なシミュレーションやランダムな待ち時間には便利ですが、セキュリティ上重要な値の生成元にはなりません。

再利用できる範囲指定整数関数を定義する

気軽な抽選を繰り返す場合は、$RANDOM を使った変換を関数にまとめられます。この関数の既定範囲は1から100です。符号付き10進数の正規形(例: -3、0、12)を境界値として受け付けます。不正な形式の引数や指定範囲外の境界値にはステータス2を返します。算術演算の前に10進数文字列を検証するため、非常に大きな値でも算術オーバーフローによって範囲チェックをすり抜けることはありません。各境界値は -1000000 から 1000000 までで、両端を含む範囲は最大2000001個の整数を含められます。$RANDOM の値を2つ組み合わせれば、この範囲に十分な元の値を用意できますが、剰余による変換には偏りが残ることがあります。

このセクションのコードと表示されるラベルや出力は英語のままです。

random_between() {
    local min=${1-1} max=${2-100} span min_digits max_digits random_value
    [[ $min =~ ^-?(0|[1-9][0-9]*)$ && $max =~ ^-?(0|[1-9][0-9]*)$ ]] || return 2
    min_digits=${min#-}
    max_digits=${max#-}
    (( ${#min_digits} <= 7 && ${#max_digits} <= 7 )) || return 2
    (( min >= -1000000 && min <= 1000000 && max >= -1000000 && max <= 1000000 )) || return 2
    (( min <= max )) || return 2
    span=$((max - min + 1))
    (( span >= 1 && span <= 2000001 )) || return 2
    random_value=$((RANDOM * 32768 + RANDOM))
    printf '%d\n' "$((random_value % span + min))"
}

random_between 10 20

この関数は両端を含みます。剰余を使うため、前述の偏りが残り、セキュリティではなく利便性のための関数です。偏りのない選択や、より広い範囲が必要な場合は、利用できれば shuf などの適切なシステムユーティリティを使ってください。秘密情報には、その用途向けに設計された暗号APIを使用してください。