ハイパーマッスルエンジニア

Vim、ShellScriptについてよく書く

Shellでランダムな文字列を生成する(文字列/数字文字列/乱数)

f:id:rasukarusan:20190329223857p:plain:w500

ふとした瞬間に「あ、ランダムな文字列生成したい」というときがあると思う。
そんなときのShellコマンドを紹介。

ランダムな文字列にも色々種類がある

今回生成するのは以下。

  • ランダムな文字列生成(例:'ex792Lhk')
  • ランダムな数字文字列生成(例:'189765')
  • 指定範囲内のランダムな整数生成(例:1〜100の数字)

ランダム系によく使われるやつ

  • /dev/random
  • /dev/urandom
  • od

上記は全てlinux系には最初から入っているっぽいので環境気にせず使える(UbuntuとかMacとか)。
randomurandomどっち使えばええんや問題があるらしいが、とりあえずurandomを使ってれば良い。「urandomは乱数種を再利用するから安全性がァー」とかあるらしいが別にセキュリティに利用するわけでもないのでおk。

ランダムな文字列生成(例:'ex792Lhk')

# ランダムな文字列を生成。第一引数に桁数を指定。デフォルトは10。
alias randomStr='_generateRandomString'
function _generateRandomString() {
    local length=${1:-10}
    cat /dev/urandom | base64 | fold -w $length | head -n 1
}

出力

$ randomStr
Ry7pAIf2De

# 20文字の文字列生成
$ randomStr 20
eiQC0aHDg18ffH6x3oUt

ランダムな数字文字列生成(例:'189765')

乱数ではなく数値文字列であることに注意。

# ランダムな数値文字列を生成。第一引数に桁数を指定。デフォルトは4。
alias randomStrNum='_generateRandomNumberStr'
function _generateRandomNumberStr() {
    local length=${1:-4}
    od -vAn -to1 </dev/urandom  | tr -d " " | fold -w $length | head -n 1
}

出力

$ randomStrNum
3311

# 10桁の数字文字列を生成
$ randomStrNum 10
3151543003

指定範囲内のランダムな整数生成(例:100を指定した場合、1〜100の数字)

# 指定範囲内のランダムな整数を生成。第一引数に範囲を指定。デフォルトは100。
alias randomNum='_generateRandomNumber'
function _generateRandomNumber() {
    local range=${1:-100}
    awk 'BEGIN{srand();print int(rand() * '"${range}"')}'
}

出力

$ randomNum
20

# 1~1000の範囲で乱数を生成
$ randomNum 1000
344

解説

cat /dev/urandom | base64 | fold -w $length | head -n 1

cat /dev/urandomと打つと以下のように文字化けしたような出力が得られる

$ cat /dev/urandom
����5>�W�R>�v��n������ڡ2C��9ϔ@{hRiNɂ|�_�7�G�b�u^q��E6�$������/}~����QC�w��{��@̝���H�Y U���$/%2  ���WNp��y�a������}�u��{���@5�ţ��*�g[!��H�U������o:yKw�
...(略)

このままだと読めないのでbase64でデコード。すると下記のように無限に文字列が出力される。Ctrl+Cで終了できる。

4HkkhYJXrKlApvoBlmcw+X8O45Xz9JHo149+vLRYEY3SYATpgzJK/uhPNwfU6YzagjpJS6JpXDhJUzdzCq3BI93AedrZ6iHRRaNnELFA4bsVpM4Aw3WyVa8VxDl4ryzh1ZTF1bKi7Std7s9hqNK6PHCKsWY90Cfzi/TmZ2fw05lgE001t8p9k3W9PX+um7fXowoinq9EVFLgAshKY4bl6slPCmPBcBrq1fa7tNUQfKdKrymlnxgYVSIBQN1vhLw5kDkh0o
...(略)

あとはお好きな方法で文字を切り取るだけ。 今回はfold -wを使って、指定文字数で区切って改行させ、head -n 1で先頭行だけ抽出させている。

od -vAn -to1 </dev/urandom | tr -d " " | fold -w $length | head -n 1

odなんてコマンドこれを調べるまで全く知らなかったが、以下のサイトが詳しい。

x68000.q-e-d.net

標準入力を8進数や16進数にダンプしてくれるらしいので、これを乱数作成に利用する。 また、od -vAn -td4など、10進数で出力してくれる-td4や16進数の-tx4をよく見るが、私は-to18進数1バイトの出力を利用している。 理由としては桁数が綺麗に出力出来る&数字のみになるから。

-td4だとマイナスが表示されてしまうし、-tx4だと文字列と数字が混合してしまう。

$ od -vAn -td4 </dev/urandom
-1821141382      -849610530     -1013568552       245312278
...

$ od -vAn -tx4 </dev/urandom
04177049        f189f437        367061e5        4e9b108e
...

あとはやってみればわかると思うが桁数を綺麗に表示するには3桁区切りの-to1が一番扱いやすい。(-to1,-to2,-to4とか試すと面白い。) tr -d " "で空白を消した時に、思ったとおりの桁数が得られたのが-to1だったってのもある。空白を消さなければ-to4でもなんでもええんちゃうか。

$ od -vAn -to1 </dev/urandom
025 054 220 145 350 345 110 041 174 245 177 253 157 302 157 304
...

awk 'BEGIN{srand();print int(rand() * '"${range}"')}'

これはもうシンプルにawkゲーですわ。便利だね、で終わらせましょう。

終わり

zshだと(bashにもあるのかな?)$RANDOMという環境変数が用意されているが、使い勝手が悪いので却下した。
ちなみにふとした瞬間に「あ、ランダムな文字列生成したい」となったことはまだ一度もない。