AWKでちょっとしたテキストを

処理する方法。
AWKとは?
・Alfred Aho/Peter Weinberger/Brain Kernighanによって
 作られたシンタックス駆動型のパターンマッチング言語。
!
・Alfred Ahoいわく「AWKは一般的なデータ処理用の短い

 プログラムを記述するために設計されたスクリプティング言語」
A W K
※O REILLY「言語設計者たちが考えること」より
AWKの(個人的に考える)メリット。
・最初からテキストファイルを行xカラムに分けられる。
!
・行に対してのパターンマッチングが行える。
!
・学習コストが低い。
こんなデータを使います。
NO DATE NAME VAL
1 2013/01/01 23:59:12 aaa 20
2 2013/04/05 12:10:09 bbb 42
3 2013/02/22 09:54:01 ccc 63
4 2013/06/19 04:45:12 abc 120
5 2013/10/09 12:22:53 bbc 21
6 2013/03/23 20:12:23 zzz 9
・スペース区切りのTestData.txt、タブ区切りのTestData.tsv
取り敢えず実行してみよう。
・ファイルの中身が全てそのまま表示される。
> awk ‘{print $0}’ TestData.txt
NO DATE NAME VAL
1 2013/01/01 23:59:12 aaa 20
2 2013/04/05 12:10:09 bbb 42
3 2013/02/22 09:54:01 ccc 63
4 2013/06/19 04:45:12 abc 120
5 2013/10/09 12:22:53 bbc 21
6 2013/03/23 20:12:23 zzz 9
AWKの構造(1)
・awkは指定されたファイルの全ての行に命令文を繰り返す。
awk ‘{print $0}’ TestData.tsv
もう一度実行してみよう。
・今度はスペース区切り2カラム目と4カラム目が表示される。
・awkは自動的にスペース区切りでカラムを区切る。
・$iでiカラム目を選択可能。
・$0は全てのカラムという意味。
・上の文はTestData.txtの2カラム目と4カラム目を表示するという命令を

 全行にわたって繰り返せという意味。
>awk ‘{print $2,$4}’ TestData.txt
DATE VAL
2013/01/01 aaa
2013/04/05 bbb
2013/02/22 ccc
2013/06/19 abc
2013/10/09 bbc
2013/03/23 zzz
AWKの構造(2)
awk ‘BEGIN{}{print $0}END{}’ TestData.tsv
FSとOFS。
・FSとOFSは特殊な変数。
・どちらも区切り文字を指定するもの。
・FSはインプットファイルの区切り文字。
・OFSはアウトプットファイルの区切り文字。
・デフォルトではどちらもスペースになっている。
tsvファイルを開いてみよう。
・BEGINの部分にFS= t を入れる事で今後行われる命令文は全て
 タブ区切りテキストを対象にしている事を伝えている。
・タブ区切りテキストもちゃんと指定したカラムが表示される。
・7枚目のスライドと結果が微妙に違う事に注意。
> awk ‘BEGIN{FS= “t” }{print $2,$4}’ TestData.tsv
DATE VAL
2013/01/01 23:59:12 20
2013/04/05 12:10:09 42
2013/02/22 09:54:01 63
2013/06/19 04:45:12 120
2013/10/09 12:22:53 21
2013/03/23 20:12:23 9
tsvファイルをcsvファイルにしてみよう。
・BEGINの部分にOFS= , を入れる事で出力されるデータが
 カンマ区切りになる。
・ BEGIN{FS= t ;OFS= , }{print $0} ではどういう結果になるか試してみよう。
> awk ‘BEGIN{FS= “t” ;OFS= “,” }{print $1,$2,$3,$4}’
TestData.tsv > TestData.csv
>cat TestData.csv
NO,DATE,NAME,VAL
1,2013/01/01 23:59:12,aaa,20
2,2013/04/05 12:10:09,bbb,42
3,2013/02/22 09:54:01,ccc,63
4,2013/06/19 04:45:12,abc,120
5,2013/10/09 12:22:53,bbc,21
6,2013/03/23 20:12:23,zzz,9
ここまでのまとめ
NRとNF。
> awk ‘BEGIN{FS= “t” }{print NR,NF,$0}’ TestData.tsv
1 4 NODATE NAME VAL
2 4 1 2013/01/01 23:59:12 aaa 20
3 4 2 2013/04/05 12:10:09 bbb 42
4 4 3 2013/02/22 09:54:01 ccc 63
5 4 4 2013/06/19 04:45:12 abc 120
6 4 5 2013/10/09 12:22:53 bbc 21
7 4 6 2013/03/23 20:12:23 zzz 9
・NRは行番号、NFはフィールド数が格納される。
ここまでのまとめ(2)
AWKの構造(3)
・中括弧の前に条件式を置くことで中括弧の中の処理を行うか

 判別が出来る。
awk ‘EXPR{print $0}’
条件式 条件式が真なら実行される命令
条件式を使ってみよう(1)
・「4カラム目が20以下」の行を出力する。
・1行目と6行目は共に20以下なので出力される。
・それ以外の行は出力されない。
>awk ‘BEGIN{FS=” t” }$4 <= 20{print $0}’
1 2013/01/01 23:59:12 aaa 20
6 2013/03/23 20:12:23 zzz 9
条件式を使ってみよう(2)
・ //で正規表現でのマッチングが可能。
・最初の式は「3カラム目にbが含まれている」行を出力。
・2番目の式は「3カラム目がcで終わっている」行を出力。
>awk ‘BEGIN{FS=” t” }$3 ~ /b/{print $0}’
2 2013/04/05 12:10:09 bbb 42
4 2013/06/19 04:45:12 abc 120
5 2013/10/09 12:22:53 bbc 21
>awk ‘BEGIN{FS=” t” }$3 ~ /c$/{print $0}’
3 2013/02/22 09:54:01 ccc 63
4 2013/06/19 04:45:12 abc 120
5 2013/10/09 12:22:53 bbc 21
条件式を使ってみよう(3)
・&&や¦¦で複数の条件を与えることも可能。
・最初の式は「3カラム目がcで終わっている」且つ「4カラム目が
 40より大きい」行を出力。
・2番目の式は「3カラム目がcで終わっている」或いは
 「4カラム目が20以下」の行を出力。
>awk ‘BEGIN{FS=” t” }$3 ~ /c$/ && $4 > 40{print $0}’
3 2013/02/22 09:54:01 ccc 63
4 2013/06/19 04:45:12 abc 120
>awk ‘BEGIN{FS=” t” }$3 ~ /c$/ || $4 <= 20{print $0}’
1 2013/01/01 23:59:12 aaa 20
3 2013/02/22 09:54:01 ccc 63
4 2013/06/19 04:45:12 abc 120
5 2013/10/09 12:22:53 bbc 21
6 2013/03/23 20:12:23 zzz 9
条件式を使ってみよう(4)
・中括弧の数だけ処理が出来る。
・上の例では$4が偶数なら even という文字列と$4を出力。
・$4が奇数なら uneven という文字列と$4を出力。
>awk ‘BEGIN{FS=” t” }$4%2==0{print “even” ,$4}
$4%2==1{print “uneven” ,$4}’
even VAL
even 20
even 42
uneven 63
even 120
uneven 21
uneven 9
まとめるとこんな感じ。
BEGIN{
print “script start”
}
EXPR1{
print “hoge1” $0
}
EXPR2{
print “hoge2” ,$0
}
{
print “all” ,$0
}
END{
print “script end”
}
行が読まれる前に実行される。
EXPR1 が真なら実行される。
EXPR2 が真なら実行される。
全ての行で実行される。
全ての行が読み終わったら実行される。
AWKの構造(4)
awk -f test.awk TestData.tsv
・-fで命令文を書いたファイルを読みこませるとプログラムを
 外部ファイルにする事が出来る。
合計を求めてみよう。
・BEGINで変数を初期化、ENDで変数を出力。
・1行目はヘッダなので合計対象から外す。
sum.awk
> awk -f sum.awk TestData.tsv
275
if。
・sum.awkに条件をひとつ追加。1カラム目が奇数の時のみ
 合計対象とする。
if.awk
> awk -f if.awk TestData.tsv
104
for。
for(i=1;i<=10;i++){print i}
初期化式
継続条件式
再初期化式
・言わずと知れたループ構文。
・iに1を代入しループを開始する。
 iに1を加えながらiが10になるまでループを繰り返す。
for。
・個人的によく使うカラムチェックスクリプト。
・forで1カラム目からNカラム目までをナンバリングして
 1行ずつ出力する。
> awk -f for.awk TestData.tsv
1:1
2:2013/01/01 23:59:12
3:aaa
4:20
for.awk
split。
split(str,array,sep)
分割する文字列
分割した結果を格納する配列
区切り文字
・文字列を分割するための命令文。
・strに分割したい文字列を、arrayに格納したい配列を、
 sepに区切り文字を入れる。
split。
split.awk
> awk -f split.awk TestData.tsv
NO DATE TIME NAME VAL
1 2013/01/01 23:59:12 aaa 20
2 2013/04/05 12:10:09 bbb 42
3 2013/02/22 09:54:01 ccc 63
4 2013/06/19 04:45:12 abc 120
5 2013/10/09 12:22:53 bbc 21
6 2013/03/23 20:12:23 zzz 9
BEGIN{
FS="t";
OFS="t";
}
NR == 1{
print $1,$2,"TIME",$3,$4;
next;
}
{
split($2,datetime," ");
print $1,datetime[1],datetime[2],$3,$4;
}
split。
・日時を日付と時間に分割するスクリプト。
・$2をスペースで分割してdatetimeに放り込む。
・datetime[n]でn番目の要素にアクセス可能。
 つまり日付はdatetime[1]、時間はdatetime[2]。
split.awk
BEGIN{
FS="t";
OFS="t";
}
NR == 1{
print $1,$2,"TIME",$3,$4;
next;
}
{
split($2,datetime," ");
print $1,datetime[1],datetime[2],$3,$4;
}
変数と配列
・変数はひとつの容れ物にひとつの値。
・配列はひとつの容れ物を幾つかに分けてラベルをつけて格納する。
val
array
※ちなみにAWKの配列は連想配列。インデックスは数値じゃなくてもOK。
条件式を使ってみよう(5)
nr.awk
> awk -f nr.awk TestData.tsv
2 2013/04/05 12:10:09 bbb 42
3 2013/02/22 09:54:01 ccc 63
4 2013/06/19 04:45:12 abc 120
5 2013/10/09 12:22:53 bbc 21
$1==2,$1==5{
print $0
}
・条件式をカンマで2つ並べると最初の式が成り立ってから
 2番目の式が成り立つまでを実行する。
・上の例だと$1==2がなりたつ3行目から$1==5がなりたつ6行目
 までを実行する。
・$1が2,3,4,5の時に実行されるわけではない事に注意。
条件式を使ってみよう(5)
・$5==20の2行目から$5==21の6行目までが出力される。
nr2.awk
> awk -f nr.awk TestData.tsv
1 2013/01/01 23:59:12 aaa 20
2 2013/04/05 12:10:09 bbb 42
3 2013/02/22 09:54:01 ccc 63
4 2013/06/19 04:45:12 abc 120
5 2013/10/09 12:22:53 bbc 21
BEGIN{
FS="t";
OFS="t";
}
$4==20,$4==21{
print $0
}
縦横置換スクリプト
transpose.awk
BEGIN{
FS="t";
OFS="t";
}
{
for(i=1;i<=NF;i++){
val[i, NR] = $i;
}
}
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
}
縦横置換スクリプト読み解き
for(i=1;i<=NF;i++){
val[i, NR] = $i;
}
↓val[1,1] ↓val[2,1] ↓val[3,1] ↓val[4,1]
この for 文が何をしているか。
例えば今までと同様 TestData.tsv に対して実行してみると…。
データの最初の 1 行目は配列 val に以下の様に格納される。
2 行目はこんな感じに格納される。
↓val[1,2] ↓val[2,2] ↓val[3,2] ↓val[4,2]
縦横置換スクリプト読み解き
for(i=1;i<=NF;i++){
val[i, NR] = $i;
}
最終的には横がフィールド、縦が行数の配列が出来上がる。
フィールド
行数
val
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=1/j=1の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=1/j=2の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=1/j=3の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=1/j=4の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=1/j=5の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=1/j=6の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=1/j=7の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=1の中のjのループが終わったのでprint “”で改行を行う。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=2/j=1の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=2/j=2の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=2/j=3の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=2/j=4の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=2/j=5の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=2/j=6の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=2/j=7の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=2の中のjのループが終わったのでprint “”で改行を行う。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=3/j=1の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=3/j=2の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=3/j=3の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=3/j=4の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=3/j=5の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=3/j=6の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=3/j=7の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=3の中のjのループが終わったのでprint “”で改行を行う。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=4/j=1の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=4/j=2の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=4/j=3の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=4/j=4の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=4/j=5の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=4/j=6の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=4/j=7の時。
縦横置換スクリプト読み解き
END{
for(i=1;i<=NF;i++){
for(j=1;j<=NR;j++){
printf("%st",val[i,j]);
}
print "";
}
出力部分のfor部分を読み解いていくと…
 i=4の中のjのループが終わったのでprint “”で改行を行う。
縦横置換スクリプト読み解き
■i=1 で出力される項目。
■i=2 で出力される項目。
■i=3 で出力される項目。
■i=4 で出力される項目。

Awk勉強会用資料