[unix] シェルを使って、テストデータ生成

2017年7月18日火曜日

awk bash linux unix シェルスクリプト

[シェルスクリプト]ランダムな数値では乱数の生成をしたけど、改めてシェルを使ってのテストデータ生成を考えてみよう。
できるだけ、デフォルトでありそうなコマンドかつ移植性のありそうな方法で。

同じ文字列の繰り返し

同じデータを繰り返し生成する場合はyesコマンドに文字列を渡して、headで行数を指定しよう。
$ yes ABCDE | head -n5
ABCDE
ABCDE
ABCDE
ABCDE
ABCDE

連続する数値を生成

1〜100のような連続データは単純にseqで生成する。0埋めしたい場合は-wオプションやprintfを使えばいい。
$ seq 3
1
2
3
$ seq -w 10
01
02
(中略)
09
10
$ seq 3 | xargs printf '%05d\n'
00001
00002
00003

seqは内部的に浮動小数点数を使っているので、大きな数を指定すると表示が指数表記になる場合がある。
$ seq 999998 1000000
999998
999999
1e+06

大きな桁数で整数表記のデータが必要であれば、bcを使うのがいいかもしれない。
$ echo 'for(i=1;i<=1000000;i++)i' | bc -q
1
2
(中略)
999999
1000000

連続する日付を生成

連続の数値データが得られれば、それをUnix時間とみなして、連続日付データも生成できる。

のようにGNU dateとBSD dateではオプションが違うので、注意しよう。
Linux版
$ date -d'20170618' '+%s'
1497711600
$ echo 'i=1497711600;while(n++ < 5){i;i+=86400}' | bc -q |
 sed -e 's/^/@/' | date -f- '+%Y-%m-%d %T'
2017-06-18 00:00:00
2017-06-19 00:00:00
2017-06-20 00:00:00
2017-06-21 00:00:00
2017-06-22 00:00:00

Mac,BSD版
$ date -j -f '%Y%m%d%H%M%S' '20170618000000' '+%s'
1497711600
$ echo 'i=1497711600;while(n++ < 5){i;i+=86400}' | bc -q  |
 xargs -I{} date -j -r{} '+%Y-%m-%d %T'
2017-06-18 00:00:00
2017-06-19 00:00:00
2017-06-20 00:00:00
2017-06-21 00:00:00
2017-06-22 00:00:00

ランダムな整数を生成

今度はランダムなデータを生成してみよう。
[シェルスクリプト]ランダムな数値ではodコマンドのオプションを使ってバイナリデータを数値に変換していたけど、文字列とかへの応用も考えるとtrで該当の文字が出現した場合のみ、拾うようにもできる。
桁数はfoldで指定する。
$ LC_CTYPE=C tr -dc '0-9' </dev/urandom | fold -w8 |
 head -n5
29573283
41462132
28552390
09252685
49857674

先頭を0埋めしたくなければ、sedで先頭の0を除去しよう。
$ LC_CTYPE=C tr -dc '0-9' </dev/urandom | fold -w8 |
 head -n5 | sed -e 's/^0*//'
29366227
63890313
20093604
2453784
13832814

ランダムな文字列を生成

同じように0-9以外の拾いたい文字をtrで指定すれば、ランダムな文字列も生成できる。
$ LC_CTYPE=C tr -dc 'A-Za-z0-9' </dev/urandom |
 fold -w8 | head -n5
oZJ1Q3Ix
E8qvDy33
WuQHDMnt
MDfo3pNt
FLMmjCfh

ランダムな日付を生成

連続日付データと同様にランダムな日付データも生成してみよう。

Linux版
$ date -d'20170618' '+%s'
1497711600
$ LC_CTYPE=C tr -dc '0-9' </dev/urandom | fold -w10 |
 sed -e 's/^0*//' | awk '$1<1497711600{ print $1 }' |
 head -n5 | sed -e 's/^/@/' | date -f- '+%Y-%m-%d %T'
1975-07-09 21:13:43
2001-12-22 01:47:22
2007-05-18 21:53:30
1984-07-13 18:06:16
1995-02-25 23:12:42

Mac,BSD版
$ date -j -f '%Y%m%d%H%M%S' '20170618000000' '+%s'
1497711600
$ LC_CTYPE=C tr -dc '0-9' </dev/urandom | fold -w10 |
 sed -e 's/^0*//' | awk '$1<1497711600{ print $1 }' |
 head -n5 | xargs -I{} date -j -r{} '+%Y-%m-%d %T'
1995-04-28 11:31:12
2008-07-07 20:33:57
1993-07-23 15:54:51
1999-05-09 19:52:59
2006-08-16 21:52:21

2017/06/18時点でのUnix時間の桁数は10桁だけど、そのままではかなり未来の日付まで登場してしまうので、awkで適当に切ってしまおう。

ランダムな浮動小数点数を生成

0〜1の浮動小数点数の乱数を生成したい場合はawkを使おう。
ただし、awkの乱数種は現在時刻から生成されるので、何度も生成する場合はsrand関数にbashの$RANDOMを渡すなりの工夫が必要となる。
$ seq 5 | awk "BEGIN{ srand($RANDOM) } { print rand() }"
0.101913
0.67829
0.169584
0.236928
0.35855

テストデータの組を生成

これらの結果をpasteコマンドでつなげば、テストデータの組を生成できる。
pasteでつなげる方法としては
  • 一時ファイルを使う
  • 名前付きパイプを使う
  • プロセス置換を使う
が考えられるけど、プロセス置換はbash固有の機能なので、スクリプトで使う場合は /bin/sh でhなく、/bin/bash にする必要がある。

Linux版
$ paste <(seq 5) \
        <(LC_CTYPE=C tr -dc 'A-Za-z0-9' </dev/urandom |
         fold -w8 | head -n5) \
        <(LC_CTYPE=C tr -dc '0-9' </dev/urandom |
         fold -w10 | sed -e 's/^0*//' |
         awk '$1<1497711600{ print $1 }' | head -n5 |
         sed -e 's/^/@/' | date -f- '+%Y-%m-%d')
1       CPyGlAdN        2017-02-12
2       0ELSHuu4        2010-03-31
3       h6NxN6WJ        1988-11-15
4       WQ6y52Gp        2005-07-05
5       sURAKOI6        1997-12-12

Mac,BSD版
$ paste <(seq 5) \
        <(LC_CTYPE=C tr -dc 'A-Za-z0-9' </dev/urandom |
         fold -w8 | head -n5) \
        <(LC_CTYPE=C tr -dc '0-9' </dev/urandom |
         fold -w10 | sed -e 's/^0*//' |
         awk '$1<1497711600{ print $1 }' | head -n5 | 
         xargs -I{} date -j -r{} '+%Y-%m-%d')
1 e1QPEVO0 2015-05-23
2 wZOdHS5o 2017-01-29
3 GPJkOIHU 1982-09-02
4 nzPg2jvz 1992-03-22
5 BZqtBtkQ 2006-11-20

CSV形式で保存したければ、pasteの-dオプションでカンマを指定すればいい。

Linux版
$ paste -d, <(seq 5) \
        <(LC_CTYPE=C tr -dc 'A-Za-z0-9' </dev/urandom |
         fold -w8 | head -n5) \
        <(LC_CTYPE=C tr -dc '0-9' </dev/urandom |
         fold -w10 | sed -e 's/^0*//' |
         awk '$1<1497711600{ print $1 }' | head -n5 |
         sed -e 's/^/@/' | date -f- '+%Y-%m-%d')
1,k3Pwocom,1996-06-16
2,j4h53ZNY,1994-10-30
3,orMT66yb,1995-08-23
4,xYQ5SVB4,2016-01-25
5,c5IF2oVe,2017-04-22

Mac,BSD版
$ paste -d, <(seq 5) \
            <(LC_CTYPE=C tr -dc 'A-Za-z0-9' </dev/urandom |
             fold -w8 | head -n5) \
            <(LC_CTYPE=C tr -dc '0-9' </dev/urandom |
             fold -w10 | sed -e 's/^0*//' |
             awk '$1<1497711600{ print $1 }' | head -n5 | 
             xargs -I{} date -j -r{} '+%Y-%m-%d')
1,zk6PpLgI,2002-11-06
2,5gN8q9H9,1996-05-23
3,FtJ3fLpk,1981-05-02
4,G5jfaiDm,1987-08-11
5,lTODWiuB,2016-04-25