LoginSignup
0
1

More than 5 years have passed since last update.

awk 歴 23 年のおっさんが 2017 年に知った awk の小技 : 「プログラムファイルの分割」と「処理対象ファイルごとの分岐」

Last updated at Posted at 2017-02-02

これは何

タイトルのとおり。23 年もずっと使い続けているスクリプト言語は awk ぐらいしかないかも。

プログラムファイルの分割

コマンドマンドラインオプション -f (--file) は複数与えられる。gawk のどのバージョンからなのか、など調べていませんが、今日初めて知りました。
ということは、プログラムファイルを分割することができますね。
もちろんネームスペースとかスコープとかカプセル化とか、そういう概念とは無縁でしょうから、単に一本のプログラムファイルに書いていたものを管理しやすいように分割するだけです。
まあ、よく使う関数群を一つのプログラムファイルに書いておいて (そのファイルは function 定義がいっぱい書いてある) あちこちから使うというのがいちばんイメージしやすいですね。ときおり BEGIN ブロックが巨大化するときがありますが、そういうときも別ファイルに括りだしておいた方がスクリプトの見通しはよくなりますね。

処理対象ファイルごとの分岐

awk -f test.awk test1.csv test2.csv みたいに複数のファイルを処理対象にして、なおかつ、その処理対象ファイルごとに違う処理をしたい場合、プログラムファイル内で FILENAME を基に分岐する処理を描くわけです。普通に書けば、

FILENAME == "test1.csv" {
    ...
}

とかそういうパターンを書くのでしょうが、FILENAME がフルパスだったりすると比較する部分がもっと長くなるし、そもそも FILENAME というのも長いし、もうちょと短く書けないかな、と考えて、さっきこういうのを思いつきました。

FNR == 1 {
    fname = gensub(/^.*\/([^\/]+)\.csv$/, "\\1", 1, FILENAME);
    delete m;
    m[fname] = 1;
}

m["test1"] {
    # test1.csv に関する処理
}

m["test2"] {
    # test2.csv に関する処理
}

ミソは、m[***] がどれか一つだけ真 (1) になることを利用して、パターンが短く書ける、というところですね。

これだとプログラムファイルが散らかる可能性があるので、たとえば、test1.csv の処理に特化した部分と test2.csv の処理に特化した部分を、さきに書いたように分割すればいいのかな、と。

testmain.awk
FNR == 1 {
    fname = gensub(/^.*\/([^\/]+)\.csv$/, "\\1", 1, FILENAME);
    delete m;
    m[fname] = 1;
}
testsub.awk
function stderr(msg) {
    print msg > "/dev/stderr";
}
test1.awk
m["test1"] && FNR < 3 {
    stderr(sprintf("[%s:%03d] %s", fname, FNR, $0));
}
test2.awk
m["test2"] && FNR % 2 == 0 {
    stderr(sprintf("[%s:%03d] %s", fname, FNR, $0));
}

こんな感じにしといて、

[takeyuki@jupiter ~]$ `(echo -n "awk "; ls -1 test*.awk | while read f; do echo -n "-f $f "; done; echo /var/test/*.csv)`

と実行するとか (↑は -f testmain.awk -f testsub.awk ... のようにすべての test○○.awk-f を前置して awk のコマンドラインオプションとして渡し、/var/test/ 直下の CSV ファイルを全部処理させます。あ、testmain.awk は test1.awk なんかより先に読み込ませないと駄目だね)。

追記 2017-02-02

m["***"] で分岐するって話、処理対象ファイルごとに分岐するときだけでなく、一つのファイルを処理するときに途中でモードが変わるケースにも使えますね。たとえば、なにがしかの方法でマークアップされたファイルがヘッダ、メイン、フッタに分かれるときに m["header"]m["footer"] を定義する、みたいな。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1