Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

GNU AWKでCSVファイルを楽々あつかう組込変数FPATと、関数のインダイレクト呼び出し

► 「シェル芸」に効く GNU AWK処方箋 第3回 (月刊『シェルスクリプトマガジン 2014年12月号(Vol.20)』より転載)

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2014/12/25 14:00

 AWKはレコードセパレーター(RS)に従いレコードを分割し、フィールドセパレーター(FS)に従いフィールドに分割する言語ですが、この手法ではCSVファイルでフィールド内にカンマを含むような場合に処理を行うことが困難でした。そこでGNU AWKでは新たに組込変数FPAT(フィールドパターン)というものを導入することで簡単に扱えるようになりました。今回はこの組込変数FPATを中心に解説していきます。

目次

組込変数FPATの導入

 通常、AWKのフィールド分割は組込変数FSにより分割を行います。この思想はとても便利で、一般的なファイルを処理するのには十分でした。ところが次に示すとおり、CSVファイルをうまく扱えません。

$ echo 'aaa,"bbb,ccc",ddd' |\
> awk -F, '{print $2}'
"bbb

 CSVファイルには「フィールド内にカンマを含む場合にはフィールドをダブルクォートで括る」というルールがありますので、第2フィールドは"bbb,ccc"になるはずですが、うまく取得できていません。こうしたCSVファイルも簡単に扱えるようにする仕組みとして導入されたのが、組込変数FPAT(フィールドパターン)です。

 組込変数FPATを用いると、フィールド内にカンマが含まれている場合でも、次のようにきれいに扱うことができます。

$ echo 'aaa,"bbb,ccc",ddd' |\
> gawk -v FPAT='([^,]+)|(\"[^\"]+\")' '{print $2}'
"bbb,ccc"

 組込変数FPATに記述するのは、フィールドの区切りのパターンではなく、フィールドそのもの(内容)のパターンです。上記の例では([^,]+)|(\"[^\"]+\")という正規表現をFPATに代入しています。この正規表現の意味は「カンマ(,)を含まない、またはダブルクォート(")で囲まれていて、その中にダブルクォートを含まない文字列」です。ちょっと複雑ですが、これによりCSVファイルを扱えるようになります。

 しかし、このパターンでは、CSVファイルの規格にある「ダブルクォートで括られた中に改行を含めることが可能」というルールに対応することはできません。

$ echo -e "aaa,\"bbb\nccc\",ddd" |\
> gawk -v FPAT='([^,]+)|(\"[^\"]+\")' '{print $2}'
"bbb
ddd

 このような中途半端な状態なのにCSVファイルを扱えると明言するのはおかしいのではないかという意見もありますが、gawkの開発は「完全を目指すのではなく9割をサクサクこなし、フィールドに改行を含むようなCSVファイルは専用のツールで処理すれば良い」という思想で進められました。特にメーリングリストなどに投稿された質問で多いものに対して機能追加されていく傾向にあり、日々便利なものになってきています。

 同様なものとして、Apacheなどのログファイルがあります。User-Agent名は一般的にダブルクォートで括られますので、同じようにして取り出すことができます。次に示すのは、Apacheのログが/var/log/httpd/access_logにあり、ログの最後の項目がUser-Agentの場合です。

$ sudo tail -f /var/log/httpd/access_log |\
> gawk -v FPAT='([^ ]+)|(\"[^\"]+\")' '{print $NF}
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"'

 また、この組込変数FPATを使ったpatsplit()関数も追加されました。patsplit()関数は分割する文字列のパターンを指定した文字列を分割する関数です。AWK標準のsplit()関数は分割する区切り(セパレータ)のパターンを指定するのに対し、patsplit()関数は分割した文字列のパターンを分割します。

$ echo 'aaa,"bbb,ccc",ddd' |\
> gawk '{patsplit($0,arr,"([^,]+)|(\"[^\"]+\")"); print arr[2]}'
"bbb,ccc"

 これにより、従来のAWKで扱いづらかったデータにも対応できるようになります。

 

多次元配列

 従来のAWKも、疑似的に多次元配列を持つことはできました。この際はカンマが8進数表記で\034の文字列となり、配列のインデックスが連接として扱われるということは、本連載の前シリーズ「AWK処方箋」の最終回で説明しました。

$ awk 'BEGIN{arr[1, 2]="aaa";print arr[1, 2]}'
aaa

$ awk 'BEGIN{arr[1, 2]="aaa";print arr[1"\034"2]}'
aaa

 こうした疑似的な多次元配列でも十分便利でしたが、gawkでは正式な多次元配列(実際には連想配列なので、正しくは多次元連想配列)が導入されました。

$ gawk 'BEGIN{arr[1][2] = "aaa"; print arr[1][2]}'
aaa

 ただし、この多次元配列は従来の疑似多次元配列とは異なります。

$ gawk 'BEGIN{arr[1][2] = "aaa"; print arr[1, 2]}'
(改行だけが表示される)

 多次元配列を走査するにはfor文を用います。次のプログラムはその例です。isarray1.awkとして保存します。

BEGIN {
    arr[1][1] = 300;
    arr[2]["Apple"] = 500;
    arr[3][1, 2] = 700;

    for (i in arr) {
        for (j in arr[i]) {
            print i, j, arr[i][j];
        }
    }
}

 実行してみます。

$ gawk -f isarray1.awk
1 1 300
2 Apple 500
3 12 700

 また、配列arrが何次元配列かを返す関数としてisarray()関数が追加されました。isarray()関数を用いると、先のプログラムは次のように書けます。実行結果は同じになります。

BEGIN {
    arr[1][1] = 300;
    arr[2]["Apple"] = 500;
    arr[3][1, 2] = 700;

    for (i in arr) {
        if (isarray(arr[i])) {
            for (j in arr[i]) {
                print i, j, arr[i][j];
            }
        }
    }
}

 この多次元配列は便利なのですが、次のような場合に気をつける必要があります。

$ gawk 'BEGIN {split("a b c", arr[1]); print arr[1][1]}'
gawk: cmd. line:1: fatal: split: second argument is not an array

 エラーになりましたが、こういう場合には最初にarr[1][1]を空として生成しておく必要があります。

$ gawk 'BEGIN {arr[1][1] = "";split("a b c", arr[1]); print arr[1][1]}'
a

 今までのAWKに慣れてきた人には使いにくいかもしれませんが、行列計算や集計を行う際には便利でしょう。

本連載が単行本になりました!

「シェル芸」に効く!AWK処方箋

Amazon  その他

「シェル芸」に効く!AWK処方箋

著者:斉藤博文
発売日:2017年1月31日(火)
価格(POD):2,160円(税込)
価格(電子書籍):1,728円(税込)

本書について

 コマンドであり軽量言語(LL)の元祖でもあって、シェルでのテキストデータ処理には便利で手放せない「AWK」の魅力と書き方、シェルコマンドと組み合わせたテクニック(シェル芸)を解説。
 プリントオンデマンド(POD)と電子書籍にて、絶賛発売中です!


  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • 斉藤 博文(サイトウヒロフミ)

    最初にAWKと出会ってから○十年、AWKの魅力に取りつかれ、勢い余って「日本 GNU AWKユーザー会」を立ち上げています。会としてOSCなどのイベントにも出展しつつ、GNU AWKの開発も手伝っています。「USP友の会」では幹事役ですが、「シェル芸勉強会」にはほぼ毎回参加して一緒に勉強しています。...

バックナンバー

連載:シェルスクリプトマガジン コラボレーション連載/「シェル芸」に効く GNU AWK処方箋
All contents copyright © 2005-2018 Shoeisha Co., Ltd. All rights reserved. ver.1.5