今回は、フィルタコマンド「awk」の実用的な使い方を紹介しながら、その特徴について説明しよう。例に挙げるのは、郵便番号検索・住所検索のスクリプトだ。

列ごとにデータを抜き出す

まず、サンプルデータとして日本郵政グループのサイトから郵便番号データをダウンロードし、Linuxで処理しやすいように文字コードを「SJIS」から「UTF-8」に変更。さらに、CSV形式からSSV形式(空白区切り)に変更したものを用意した。用意したデータは、次のようになっている(一部抜粋)。


% head KEN_ALL_ROME.SSV
0600000 北海道 札幌市 中央区 以下に掲載がない場合 HOKKAIDO SAPPORO_SHI_CHUO_KU IKANIKEISAIGANAIBAAI
0640941 北海道 札幌市 中央区 旭ケ丘 HOKKAIDO SAPPORO_SHI_CHUO_KU ASAHIGAOKA
0600041 北海道 札幌市 中央区 大通東 HOKKAIDO SAPPORO_SHI_CHUO_KU ODORIHIGASHI
0600042 北海道 札幌市 中央区 大通西(1~19丁目) HOKKAIDO SAPPORO_SHI_CHUO_KU ODORINISHI(1-19-CHOME)
0640820 北海道 札幌市 中央区 大通西(20~28丁目) HOKKAIDO SAPPORO_SHI_CHUO_KU ODORINISHI(20-28-CHOME)
0600031 北海道 札幌市 中央区 北一条東 HOKKAIDO SAPPORO_SHI_CHUO_KU KITA1-JOHIGASHI
0600001 北海道 札幌市 中央区 北一条西(1~19丁目) HOKKAIDO SAPPORO_SHI_CHUO_KU KITA1-JONISHI(1-19-CHOME)
0640821 北海道 札幌市 中央区 北一条西(20~28丁目) HOKKAIDO SAPPORO_SHI_CHUO_KU KITA1-JONISHI(20-28-CHOME)
0600032 北海道 札幌市 中央区 北二条東 HOKKAIDO SAPPORO_SHI_CHUO_KU KITA2-JOHIGASHI
0600002 北海道 札幌市 中央区 北二条西(1~19丁目) HOKKAIDO SAPPORO_SHI_CHUO_KU KITA2-JONISHI(1-19-CHOME)
%

それでは、ここから列ごとにデータを抜き出してみよう。


% head -5 KEN_ALL_ROME.SSV | awk '{print $1}'
0600000
0640941
0600041
0600042
0640820
% head -5 KEN_ALL_ROME.SSV | awk '{print $2}'
北海道
北海道
北海道
北海道
北海道
% head -5 KEN_ALL_ROME.SSV | awk '{print $3}'
札幌市 中央区
札幌市 中央区
札幌市 中央区
札幌市 中央区
札幌市 中央区
% head -5 KEN_ALL_ROME.SSV | awk '{print $4}'
以下に掲載がない場合
旭ケ丘
大通東
大通西(1〜19丁目)
大通西(20〜28丁目)
% head -5 KEN_ALL_ROME.SSV | awk '{print $5}'
HOKKAIDO
HOKKAIDO
HOKKAIDO
HOKKAIDO
HOKKAIDO
% head -5 KEN_ALL_ROME.SSV | awk '{print $6}'
SAPPORO_SHI_CHUO_KU
SAPPORO_SHI_CHUO_KU
SAPPORO_SHI_CHUO_KU
SAPPORO_SHI_CHUO_KU
SAPPORO_SHI_CHUO_KU
% head -5 KEN_ALL_ROME.SSV | awk '{print $7}'
IKANIKEISAIGANAIBAAI
ASAHIGAOKA
ODORIHIGASHI
ODORINISHI(1-19-CHOME)
ODORINISHI(20-28-CHOME)
%

こうすると、1列目は郵便番号、2列目は都道府県、3列目は住所1、4列目は住所2になっており、5列目から7列目までは住所の読みがローマ字で書いてあることがわかる。

awkでは、処理対象となる行を最初に選定し、次にアクションを実行する。つまり、この機能を利用すると「郵便番号で一致させてからその住所を出力させる」とか、「住所から対応する郵便番号を出力させる」といったことが簡単に実現できるのだ。

次のスクリプトをご覧いただきたい。


#!/bin/sh

key="$1"
awk '$1~/'$1'/ {print $1,$2,$3,$4}' KEN_ALL_ROME.SSV

これは、「引数に指定された文字列から、その文字列を郵便番号に含む住所を表示する」という処理である。比較処理が「’$1~/’$1’/」となっているところがポイントだ。クォートなどを取ってわかりやすく書くと、「$1~/スクリプトの第1引数/」となる。

比較が「$1==”スクリプトの第1引数”」ではなく「$1~/スクリプトの第1引数/」となっているところに注目してほしい。この記述では、正規表現でのパターンマッチングを行っているのだ。つまり、「第1引数の文字列を含む列」という指定となり、一致範囲が広がることになる。

「search_address」を実行すると次のようになる。指定した番号を含むいくつかの行が一致していることがおわかりいただけるだろう。


% ./search_address 12345
3212345 栃木県 日光市 木和田島
3812345 長野県 長野市 信更町 涌池
%
% ./search_address 2345
9862345 宮城県 石巻市 狐崎浜
9872345 宮城県 栗原市 一迫 屋敷田
9902345 山形県 山形市 富神台
3002345 茨城県 つくばみらい市 長渡呂
3212345 栃木県 日光市 木和田島
3702345 群馬県 富岡市 上黒岩
9392345 富山県 富山市 八尾町 西新町
9272345 石川県 輪島市 門前町 赤神
9202345 石川県 白山市 三ツ屋野町
9102345 福井県 福井市 間戸町
3812345 長野県 長野市 信更町 涌池
4702345 愛知県 知多郡 武豊町 西門
6752345 兵庫県 加西市 西剣坂町
6692345 兵庫県 篠山市 東岡屋
6392345 奈良県 御所市 西佐味
6302345 奈良県 山辺郡 山添村 菅生
6892345 鳥取県 東伯郡 琴浦町 福永
7092345 岡山県 加賀郡 吉備中央町 平岡
%

今度はこれをちょっと書き換えて、住所を基に郵便番号を取得するスクリプトを書いてみよう。次の記述では、比較部分が「$2$3$4~/’$1’/」となっているところがポイントだ。


#!/bin/sh

key="$1"
awk '$2$3$4~/'$1'/ {print $1,$2,$3,$4}' KEN_ALL_ROME.SSV

前回説明したように、awkコマンドでは「$N」を区切りなしで書くと、文字列の結合が行われる。つまり、この指定で「都道府県住所1住所2のなかに指定した第1引数の文字列が含まれている行を出力せよ」という指定になっている。

「search_zip」を実行すると、次のようになる。


% ./search_zip 次郎
9811526 宮城県 角田市 神次郎
9610091 福島県 白河市 弥次郎窪
9640808 福島県 二本松市 木藤次郎内
3212116 栃木県 宇都宮市 徳次郎町
9501433 新潟県 新潟市 南区 次郎右エ門興野
9591943 新潟県 阿賀野市 次郎丸
9391802 富山県 南砺市 北野(次郎丸)
9188227 福井県 福井市 次郎丸町
9120067 福井県 大野市 右近次郎
9190745 福井県 あわら市 次郎丸
4313304 静岡県 浜松市 天竜区 次郎八新田
6760063 兵庫県 高砂市 高砂町 次郎助町
6408444 和歌山県 和歌山市 次郎丸
7711702 徳島県 阿波市 阿波町 大次郎
7992646 愛媛県 松山市 神次郎町
7871551 高知県 四万十市 住次郎
8140165 福岡県 福岡市 早良区 次郎丸
8900062 鹿児島県 鹿児島市 与次郎
%
% ./search_zip 両替
4200032 静岡県 静岡市 葵区 両替町
6040865 京都府 京都市 中京区 町通室町東入,両替町通室町下る)
6008436 京都府 京都市 下京区 元両替町
6128082 京都府 京都市 伏見区 両替町
%

ご覧のとおり、「指定した文字列が住所に含まれている行」が出力されている。

今回紹介したのはいずれも簡単なサンプルだが、awkコマンドでどのようなことができるのか、基本的なところはおわかりいただけたのではないだろうか。「grep」や「sed」と異なり、awkコマンドでは処理対象に「列」という概念が入っているところが大きな特徴だ。空白区切りで列としてデータを比較したり、出力したりできるため、今回のような処理を簡単に実現できる。これは、想像以上に強力な機能だ。