mattintosh note

どこかのエンジニアモドキの備忘録

シェルで記号入りのランダムな文字列を生成する

ネットでパスワード生成出来るサービスあるじゃないですか?アレって生成結果保存してどっかに売ってたりするのかなとか思ったのでローカルでパスワードを生成することにしましたっていうだけの話。あまり精度は求めていません。

基本は前回と同じですでパターンに記号を追加しただけです。

mattintosh.hatenablog.com

Ascii の文字コードに関しては 8 進、10 進、16 進とありますが、ここでは 10 進を用います。0 から 127 までの間で文字として使えるのは 33 から 126 までです。

$ ascii -d
    0 NUL    16 DLE    32      48 0    64 @    80 P    96 `   112 p
    1 SOH    17 DC1    33 !    49 1    65 A    81 Q    97 a   113 q
    2 STX    18 DC2    34 "    50 2    66 B    82 R    98 b   114 r
    3 ETX    19 DC3    35 #    51 3    67 C    83 S    99 c   115 s
    4 EOT    20 DC4    36 $    52 4    68 D    84 T   100 d   116 t
    5 ENQ    21 NAK    37 %    53 5    69 E    85 U   101 e   117 u
    6 ACK    22 SYN    38 &    54 6    70 F    86 V   102 f   118 v
    7 BEL    23 ETB    39 '    55 7    71 G    87 W   103 g   119 w
    8 BS     24 CAN    40 (    56 8    72 H    88 X   104 h   120 x
    9 HT     25 EM     41 )    57 9    73 I    89 Y   105 i   121 y
   10 LF     26 SUB    42 *    58 :    74 J    90 Z   106 j   122 z
   11 VT     27 ESC    43 +    59 ;    75 K    91 [   107 k   123 {
   12 FF     28 FS     44 ,    60 <    76 L    92 \   108 l   124 |
   13 CR     29 GS     45 -    61 =    77 M    93 ]   109 m   125 }
   14 SO     30 RS     46 .    62 >    78 N    94 ^   110 n   126 ~
   15 SI     31 US     47 /    63 ?    79 O    95 _   111 o   127 DEL

実際に 33 から 126 までの文字を並べてみると下記のようになります。今回はこれらの文字列を使います。

!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

ランダム文字列を生成する際に上記の文字列を変数として呼び出すので変数として登録しますが、この文字列を変数に格納しようと下記のようにコードを書いても正しく動きません。

str=!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

いくつか書き方がありますが、ここでは $'' 記法を使います。文字列のうち、シングルクォートのみ、バックスラッシュでエスケープしておく必要があります。

str=$'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~'
    ^^      ^                                                                                        ^

文字列ではなく、コマンドの実行結果で格納したい場合は enumjot コマンドなどを使うのが手っ取り早いと思います。上記の文字列もこのコマンドで出力したものですがこの二つのコマンドは seq コマンドには無い、文字で出力するというオプションがあります。

(これらのコマンドのランダムオプションでも生成出来ますが遅いので今回はシェルのビルトインコマンドで行います)

enumLinux

enum -s "" -c START END
enum -s "" -c 33 126
str=$(enum -s "" -c 33 126)

jotmacOS

jot -s "" -c COUNT START
jot -s "" -c 94 33
str=$(jot -s "" -c 94 33)

文字列を格納したら欲しい文字列長でループを回して1文字ずつ取り出します。

length=16
for ((i=0; i<length; i++))
do
    printf '%s' "${p:$((RANDOM%${#p})):1}"
done

適当に rand という関数を作成します。

rand(){
    length=$1
    for ((i=0; i<length; i++))
    do
        printf '%s' "${p:$((RANDOM%${#p})):1}"
    done
}

何回か回してみます。

for ((i=0; i<10; i++)); do rand 16; echo; done
LIB)SyOH_RC[[QC[
X=b.'?Xw1S1_&"q~
-2#2X+aoaSWbNPAB
9_C7+v3gYSE?w~7?
kSc;]<<9+T3BgNea
x"%@0(E1[.!}2}5&
LtD|;sOa-.G#ZM+H
XCd#l9XY]/5^${YE
-8&'@%)Q//#aMKOg
9dE,qp3"`/I>uz},

同じ文字が連続しない文字列が欲しければ grep なんかで除外。

for ((i=0; i<10; i++)); do rand 16; echo; done | grep -v -E '(.)\1'

enum で生成する

LEFT .. COUNTx STEP .. RIGHT がちょっと変わった書式です。

$ i=32
$ enum -c -r -s "" 34 .. ${i}x .. 126
=%FU,r@Z4vXL~Y5";Xj[t%R9pY&7m$x,

jot で生成する

$ i=32
$ jot -c -r -s "" ${i} 33 126
iacA}9P}T3CFsqEa>:#'N{%#AhqBMtQV