1. ホーム
  2. スクリプト・コラム
  3. リナックスシェル

シェルで乱数を生成する7つの方法

2022-02-08 01:36:32

I. 問題点

シェルは時々乱数を使う必要があるので、乱数の生成方法をまとめておきます。コンピュータが生成するのは、絶対乱数(理想的な乱数)ではなく、"擬似乱数"です。また、疑似乱数は大量に再現しても一意性を保つとは限りませんが、優れた疑似乱数生成アルゴリズムでは、非常に長い非反復の列が生成されます。

II. 乱数

1. 乱数を生成する7つの方法

(1) 内部システム変数($RANDOM)を介する場合

echo $RANDOM

0から32767までの整数を乱数で生成し、5以上の場合は10桁の固定整数を加算し、余りを計算する。

400000〜500000の乱数を発生させる。

#! /bin/bash  
function rand(){ 
 min=$1 
 max=$(($2-$min+1)) 
 num=$(($RANDOM+1000000000)) # add a 10-bit number and remainder 
 echo $(($num%$max+$min)) 
}  
rnd=$(rand 400000 500000) 
echo $rnd  
exit 0

(2) awk によるランダム関数

awk 'BEGIN{srand();print rand()*1000000}' # can add if judgment



(3) 乱数生成のためのopenssl rand

openssl randは、指定された長さのバイトのランダム文字を生成するために使用されます。-base64または-hexはランダムな文字列をbase64でエンコードするか、hexフォーマットで表示します。

openssl rand -base64 8 | md5sum | cut -c1-8 #a combination of eight letters and numbers, 3a61800e
openssl rand -base64 8 | cksum | cut -c1-8 #Eight digits, 10784736

(4) 時刻(日付)による乱数取得

date +%s%N #Generate 19-digit number, 1287764807051101270
date +%s%N | cut -c6-13 #Take eight digits, 21793709
date +%s%N | md5sum | head -c 8 # Combination of eight letters and numbers, 87022fda



1から50までの乱数を生成する。

#! /bin/bash 
 
function rand(){ 
 min=$1 
 max=$(($2-$min+1)) 
 num=$(date +%s%N) 
 echo $(($num%$max+$min)) 
}  
rnd=$(rand 1 50) 
echo $rnd  
exit 0 


(5) システム内の固有データからの乱数生成(/dev/random、/dev/urandom)

/dev/randomには、現在システムが動作している環境に関するリアルタイムデータが格納されており、ある時点でのシステムの固有値データと見なすことができ、質の高い乱数を提供することができます。

/dev/urandomは、ブロッキングせずに読み出す乱数発生器で、高速で安全性の低い乱数発生器です。

cat /dev/urandom | head -n 10 | md5sum | head -c 10 #32f1e953ac
cat /dev/urandom | strings -n 8 | head -n 1 #Generate a full character random string, 08?WU$ZU
cat /dev/urandom | sed -e 's/[^a-zA-Z0-9]//g' | strings -n 8 | head -n 1 #Generate a random string of numbers plus letters, Ql2q9CXS, where strings -n sets the number of characters in the string and head -n sets the number of lines in the output.
head -200/dev/urandom| cksum |cut-d" " -f1 #urandom has a lot of data using cat will be slow, here use head to read 200 lines, cksum will read the contents of the file to generate a unique representation of the integer data, cut to " " split and then get the first field of the split data

(6) linuxのuuidコードを読み込む

   UUIDコードの正式名称はUniversally Unique Identifier (UUID)で、UUIDのフォーマットは、8-4-4-4-12の32文字の形で、"-"接続番号で5セグメントに分けられた、32の16進数で構成されています。linuxのUUIDコードはカーネルからも提供されており、/proc/sys/kernel/random/uuidというファイルに入っています。

 cat /proc/sys/kernel/random/uuid| cksum | cut -f1 -d" " # Get a different random integer, 1675034933
 cat /proc/sys/kernel/random/uuid| md5sum | cut -c1-8 #Number plus letter random number, d69a7ebf

linux uuid を使って 100 から 500 までの乱数を生成しています。

#! /bin/bash 
function rand(){ 
 min=$1 
 max=$(($2-$min+1)) 
 num=$(cat /proc/sys/kernel/random/uuid | cksum | awk -F ' ' '{print $1}') 
 echo $(($num%$max+$min)) 
}  
rnd=$(rand 100 500) 
echo $rnd  
exit 0

(7) 要素プールからランダムに選択する

pool=(a b c d e f g h i j k l m n o p q r s t 1 2 3 4 5 6 7 8 9 10)
num=${#pool[*]}
result=${pool[$((RANDOM%num))]}

カスタムプールの要素を使って、数字や文字を含む特定の長さの文字列を生成するために使用します。

#! /bin/bash 
length=8 
i=1 
seq=(0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) 
num_seq=${#seq[@]} 
 
while [ "$i" -le "$length" ] 
do 
 seqrand[$i]=${seq[$((RANDOM%num_seq))]} 
 let "i=i+1" 
done 
 
echo "The random string is:" 
for j in ${seqrand[@]} 
do 
 echo -n $j 
done 
echo 

2. 乱数アプリケーション

(1) 乱数は、コンピュータシミュレーション、データ暗号化、オンラインゲームなど、インターネット上で広く利用されている。一部の掲示板やゲームにログインする際、システムが乱数や文字からなる画像を生成し、ユーザーが正しく入力する必要があるが、画像形式の文字は解読されにくいため、悪意ある攻撃を防ぐのに有効である。重要なテクニックは、乱数を生成し、その文字列をASP.NETなどのツールを使って画像形式にカプセル化し、認証画像として使用することである。

(2)オンラインゲームでも、サイコロを振ったり、カードを配ったりと、乱数がよく使われている。以下は、サイコロを1000回連続で振って、1点から6点までカウントした場合の回数です。

#! /bin/bash 
#RANDOM=$$ 
PIPS=6 
MAX=1000 
throw=1 
 
one=0 
two=0 
three=0 
four=0 
five=0 
six=0 
count() 
{ 
case "$1" in 
 0) let "one=one+1";; 
 1) let "two=two+1";; 
 2) let "three=three+1";; 
 3) let "four=four+1";; 
 4) let "five=five+1";; 
 5) let "six=six+1";; 
esac 
} 
 
while [ "$throw" -le "$MAX" ] 
do 
 let "dice=RANDOM % $PIPS" 
 count $dice 
 let "throw=throw+1" 
done 
 
echo "The statistics results are as follows:" 
echo "one=$one" 
echo "two=$two" 
echo "three=$three" 
echo "four=$four" 
echo "five=$five" 
echo "six=$six" 

RANDOMは、基本的に平均値の周りを浮遊する(つまり分散が小さい)乱数を生成します。

(3) ランダムなパスワードのシステムアカウント10個の一括作成

まず、ユーザーのパスワードを指定するスクリプトを見てみましょう。

#! /bin/bash 
#Batch create 10 system accounts and set passwords, with the same account and password 
for name in `seq -w 10` 
do 
 #Non-interactive password entry 
 useradd linux$name && echo "linux$name" | passwd --stdin linux$name 
done 


linux-01からlinux-10まで、同じユーザー名とパスワードを持つ10人のユーザーがいる状態で、ランダムなユーザーパスワードを生成するスクリプトを見てみましょう。

#! /bin/bash 
#Batch create 10 system accounts and set passwords 
rm -f user.log 
for name in `seq -w 10` 
do 
 #Non-interactively enter a random password 
 password=`echo $RANDOM | md5sum | cut -c1-8` 
 #You can use password=`echo "date $RANDOM" | md5sum | cut -c3-11` 
 #You can also use password=`penssl rand -base64 8 | md5sum | cut -c1-8` 
 useradd linux$name && echo password | passwd --stdin linux$name 
 echo -e "user=linux$name \t passwd=$password" >> user.log #Save username password for review 
done 


この比較から、ランダムに生成されたパスワードの柔軟性と機密性がわかります。管理者はuser.logファイルを開き、先ほど作成した10人のユーザーに関する情報を記録することができます。

III. 概要

(1) 擬似乱数を生成するシェル関数$RANDOMは、より均等な分布を持つ擬似乱数を簡単に生成でき、ほとんどのアプリケーションのニーズを満たすことができる。

(2) 乱数生成の方法は他にもたくさんあり、拡張も可能なので、思考を拡張して最も近い方法を選択しましょう。

シェルで乱数を生成する7つの方法については、今回で終了です。シェルでの乱数についてもっと知りたい方は、スクリプトハウスの過去記事を検索するか、以下の記事を引き続きご覧ください。