前回までに、フィルタコマンド「awk」は、これまでに取り上げてきた便利コマンドと比べると、かなりプログラミング言語色が強いことを紹介した。awkコマンドでできることはまだまだたくさんあるのだが,この連載はawkの連載ではないのでこの辺りでawkの説明はおしまいにしたい。最後は、「連想配列」について触れておこう。

連想配列

今回は、前回までに取り上げたコードを連想配列を使って書き換えたものを先に示す。次のコードでは、前回までと同じ「指定した文字列が住所に含まれている行」の出力と、出現回数の集計が行われる。


#!/bin/sh

key="$1"
awk '
BEGIN {
        s = "愛知県 青森県 秋田県 石川県 茨城県 岩手県 愛媛県 " \
        "大分県 大阪府 岡山県 沖縄県 香川県 鹿児島県 神奈川県 岐阜県 " \
        "京都府 熊本県 群馬県 高知県 埼玉県 佐賀県 滋賀県 静岡県 " \
        "島根県 千葉県 東京都 徳島県 栃木県 鳥取県 富山県 長崎県 " \
        "長野県 奈良県 新潟県 兵庫県 広島県 福井県 福岡県 福島県 " \
        "北海道 三重県 宮城県 宮崎県 山形県 山口県 山梨県 和歌山県"
        split(s, ken);

        printf("====================================================\n")
        printf("\t\t住所から郵便番号を検索\n")
        printf("====================================================\n\n")
        printf("検索キーワード: %s\n\n","'$key'")
        printf("候補\t郵便番号 住所\n")
}

$2$3$4~/'$key'/ {
        printf("%03d\t%d (%s%s%s)\n",++i,$1,$2,$3,$4)
        count[$2] += 1
}

END {
        printf("\n県別集計\n")
        for (name in count)
                printf("%s  \t%d件\n",name,count[name])
        printf("\n総候補数\t%d件\n",i)
}
' KEN_ALL_ROME.SSV

これまで、awkの配列のインデックスに数字を書いてきたが、実はawkはインデックスを文字列として扱っている。このように数値以外のデータ型を使える配列を連想配列と呼ぶのだが、これがなかなか強力だ。

前回までのスクリプトでは、都道府県ごとに一致数をカウントするために配列を使い、対応するインデックスとして1から48までを割り当ててきた。しかし、awkではインデックスを文字列として扱っているので、そのまま都道府県名をインデックスにすることができるのだ。

この仕組みを使ってカウントしているのが、上述したコードの次の部分だ。


$2$3$4~/'$key'/ {
        printf("%03d\t%d (%s%s%s)\n",++i,$1,$2,$3,$4)
        count[$2] += 1
}

ここでは、「$2」に都道府県名が入っているので、「count[$2] += 1」という処理が「count[都道府県名] += 1」という処理になる。つまり、「count[“福井県”] += 1」とか、「count[“新潟県”] += 1」といった積算処理が行われていることになる。

そして、その集計データを出力しているのが次の部分だ。


        for (name in count)
                printf("%s  \t%d件\n",name,count[name])

まず、ここではこれまでとは違ったfor制御構文の使い方をしている。「for (name in count)」の部分は「for (変数 in 配列)」ということであり、「配列のインデックスを順次変数に代入して処理する」という意味だ。

つまり、「count[$2] += 1」の部分で「count[都道府県名] += 1」のようなカウントが実施され、「for (name in count)」で「name」にはカウントアップが実行された都道府県名が順次代入されて処理が行われることになる。

nameには都道府県名が入っているので、「count[name]」で対象の都道府県名の積算数にアクセスすることになる。初期値はそれぞれ、文字列は「”“」、数字は「0」と決まっているため、明示的に設定する必要はない。

スクリプトの実行結果は、これまでと同じである。


% ./sesearch_zip_2 次郎
====================================================
                住所から郵便番号を検索
====================================================

検索キーワード: 次郎

候補      郵便番号 住所
001     9811526 (宮城県角田市神次郎)
002     9610091 (福島県白河市弥次郎窪)
003     9640808 (福島県二本松市木藤次郎内)
004     3212116 (栃木県宇都宮市徳次郎町)
005     9501433 (新潟県新潟市 南区次郎右エ門興野)
006     9591943 (新潟県阿賀野市次郎丸)
007     9391802 (富山県南砺市北野(次郎丸))
008     9188227 (福井県福井市次郎丸町)
009     9120067 (福井県大野市右近次郎)
010     9190745 (福井県あわら市次郎丸)
011     4313304 (静岡県浜松市 天竜区次郎八新田)
012     6760063 (兵庫県高砂市高砂町 次郎助町)
013     6408444 (和歌山県和歌山市次郎丸)
014     7711702 (徳島県阿波市阿波町 大次郎)
015     7992646 (愛媛県松山市神次郎町)
016     7871551 (高知県四万十市住次郎)
017     8140165 (福岡県福岡市 早良区次郎丸)
018     8900062 (鹿児島県鹿児島市与次郎)

県別集計
宮城県     1件
新潟県     2件
福島県     2件
徳島県     1件
愛媛県     1件
栃木県     1件
鹿児島県    1件
富山県     1件
福岡県     1件
高知県     1件
兵庫県     1件
静岡県     1件
和歌山県    1件
福井県     3件

総候補数    18件
%

なお、前回のスクリプトとは集計結果の表示順序が異なるはずだ。これは、「for (変数 in 配列)」のような使い方をした場合の表示順が実装依存になっているためである。ここの順序は決まっていない。

awkを取り上げるのは、このくらいにしておこうと思う。awkを使うと、文字列の加工や集計が比較的簡単にできることがおわかりいただけたのではないだろうか。Linuxサーバの管理だけでなく業務データの処理などにも使えるし、使い方を知っておいて損はないコマンドだ。もし興味を持ってもらえたのなら、ぜひ一度使ってみていただきたい。