length関数の扱い
何年も前の話ですが、GNU AWKを広めたものにjgawk(Japanized GNU AWK)というものがあります。当時、GNU AWKのlength関数はバイト数を返していました。そこで、jgawkではjlengthという文字数を返す関数を導入することで日本語に対応していました。
同様な対応がGNU AWKのlength関数でも行われ、GNU AWKのlength関数はバイト数ではなく文字数を返すように変更されています。
$ echo "あいう" | gawk '{print length($0)}'
3
これは非常に便利なのですが、AWKの得意とすると言われている文書整形で日本語の折り返しを行うには文字数と文字幅が必要になります。残念ながら、これを解決できる手法をGNU AWKは持ち合わせていません。多くのLinuxでは文字コードの標準をUTF-8としたため、表示する文字幅はAWKのような処理系だけではなく表示する端末側にも依存してしまいます。そのため、最適な落としどころが見つかっていません。
もちろん、この文字単位の扱いはlength関数だけでなく、他の全ての関数にも当てはまります。substr関数の例を以下に示します。
$ echo "あいう" | gawk '{print substr($0, 2, 1)}'
い
引数版 match関数
GNU AWKでは互換性を保つために関数の引数を増やすことで拡張したものがあります。その例がmatch関数です。グルーピングされた正規表現にマッチするものを取り出したい場合に、match関数の第3引数に多次元配列名を指定します。もちろん、第3引数を用いない場合には、今までどおりのmatch関数と同じ挙動を示します。
ここで文字列universal shell programmingの頭文字を抜き出す例を挙げておきます。
BEGIN {
str = "universal shell programming";
match(str, /([^ ]).+[ ]([^ ]).+[ ]([^ ]).+/, parts);
for (i = 1; i <= 3; i++) {
printf("%s", toupper(substr(str, parts[i, "start"], parts[i, "length"])));
}
print "";
}
match.awkというファイル名で保存し実行すると、以下のようになります。
$ gawk -f match.awk USP
match関数の第2引数の正規表現に注目してください。ここで丸括弧で括りグルーピングしているものをそれぞれ第3引数である多次元配列partsに格納しています。i番目の開始位置がparts[i, "start"]に格納され、長さがparts[i, "length"]に格納されます。
連想配列のfor ~ inでの呼び出し
GNU AWKでは組込配列PROCINFOに対して設定を行うことで、他に影響を及ぼさずに拡張するという案が取られています。これにより余分な組込変数を増やすことなく拡張がはかられています。
前回、連想配列の呼び出しでfor ~ inを用いると単にハッシュテーブルの呼び出しになるため、呼び出しの順序を固定することができないという話を書きました。インデックスを文字列としてソートする場合には以下のように組込配列PROCINFOを使って以下のようにします。
BEGIN {
value_of["Apple"] = 100;
value_of["Orange"] = 200;
value_of["Banana"] = 300;
PROCINFO["sorted_in"] = "@ind_str_asc";
for (i in value_of) {
print i, value_of[i];
}
}
この組込配列PROC_INFO["sorted_in"]は先頭の@を除き3つのパートに分かれます。最初がインデックス(ind)なのか値(val)なのか、次が文字列(str)なのか数字(num)なのか、昇順(asc)なのか降順(desc)なのかです。
したがって、降順の場合には以下のように記述します。
BEGIN {
value_of["Apple"] = 100;
value_of["Orange"] = 200;
value_of["Banana"] = 300;
PROCINFO["sorted_in"] = "@val_num_desc";
for (i in value_of) {
print i, value_of[i];
}
}
test.awkとして保存して実行すると以下のようになり、指定通りに降順になっていることが分かります。
$ gawk -f test.awk Banana 300 Orange 200 Apple 100
この組込配列PROCINFO["sorted_in"]が導入される以前に、配列の値をソートして配列として代入するasort関数とインデックスをソートして配列として代入するasorti関数が導入されました。
BEGIN {
value_of["Apple"] = 100;
value_of["Orange"] = 200;
value_of["Banana"] = 300;
asorti(value_of);
for (i in value_of) {
print i, value_of[i];
}
}
実行例を以下に示します。
$ gawk -f test.awk 1 Apple 2 Banana 3 Orange
asort関数は元の連想配列の値を通常の配列としてソートした形で格納し、asorti関数は元の連想配列のインデックスを通常の配列としてソートした形で格納するため、連想配列を操作するには扱いにくい関数です。今回の組込配列PROCINFO["sorted_in"]の導入で随分扱いやすくなったのではないでしょうか。
まとめ
GNU AWKの拡張は一言で言えば「人の直感」に近い拡張です。特にシステムの根幹で昔から動作しているAWKプログラムが多くあるため、互換性に注意して開発されています。そのため、match関数のように追加の引数を導入したり、組込配列PROCINFOを導入し、今まで動作しているプログラムに影響を及ぼさないように開発が進められています。特にa-zやA-Zのような文字集合を示す正規表現の拡張は他のGNUのツールに対してどのような影響を与えるか注目です。
『シェルスクリプトマガジン 2014 November(Vol.19)』は、ただいま絶賛発売中!

『シェルスクリプトマガジン』は日本で唯一のシェルスクリプト総合誌(毎月25日発売)。最新号 2014 November(Vol.19)の巻頭特集は特別対談「熟年エンジニア2人が語る 若手エンジニアに伝えたいこと」。エンジニアにもっと幸せになってもらいたい、それには何が必要か。過去・現在・未来の視点からこれまでとこれからのITについて語っていただきました。平さんの「リナ女のたしなみ」では、Amazon Glacierを利用した、写真のバックアップを紹介。長谷川さんの「うにっくすなやつら」もシェルスクリプトネタでゆるふわ復活です! 本連載「GNU AWK処方箋」の第2回はレイアウトを一新。たくさんのオオウミガラスと共にGNU拡張された関数を盛りだくさんの内容で解説します。他にも「ユニケージ開発手法 コードレビュー」や「漢のUNIX」など好評連載中です!
► お求めは、Amazon.co.jp、USP研究所のWebサイト、取り扱い書店まで。
シェルスクリプトマガジン 2014 November(Vol.19)
価格:500円+税
仕様:B5版 64ページ オールカラー
発行人:ユニバーサル・シェル・プログラミング研究所
