BEGINFILEとENDFILE
gawkでは、従来のAWKのBEGIN部とEND部に加えて、BEGINFILE部とENDFILE部が追加されました。ここではBEGIN部とBEGINFILE部の違いについて説明しておきます。
さて、BEGIN部とBEGINFILE部は何が違うのでしょうか。BEGIN部もBEGINFILE部も入力ファイルを読み込む前に実行される点では同じですが、BEGINFILE部ではファイルがオープンされています。つまり、ファイルの有無のチェックをBEGINFILE部で行うことができます。
具体例で示しましょう。次のようなプログラムをbeginfile.awkとして保存します。
BEGINFILE {
if (ERRNO) {
print "File not exist.";
}
exit;
}
このプログラムを、実際に存在しないファイルを指定して実行します。ここでは引数のfile_not_existというファイルは存在しないものとします。
$ gawk -f beginfile.awk file_not_exist File not exist.
従来は入力ファイルが存在しないということで、gawk自体がエラーを出してプログラムの実行が停止していました。しかし、この例のようにBEGINFILE部を記述することにより、プログラム内でファイルの有無を判定できるようになります。
もちろん、これまでも次のように記述することでファイルの有無を確認できましたが、やはりスマートさに欠けているといえるでしょう。
BEGIN {
if (getline < ARGV[1] <= 0) {
print "File not exist.";
}
close(ARGV[1]);
}
getline文は特殊なことや、その後には必ずclose()関数でクローズする必要があり、扱いにくいものでしたが、BEGINFILE部の導入で楽になりました。
Indirect Function Call
Indirect Function Callとは聞きなれない言葉なので、あえて英語表記のままにしますが、変数値と同じ関数を呼び出す仕組みです。「@変数名()」という記述で、変数の値を関数名と解釈し、その関数を呼び出します。 使いどころが難しいため、良い例ではないのですが、以下にIndirect Function Callの使用例を示します。「gu -> choki」というように、じゃんけんの手とそれに勝つ手を表示するもので、変数varの値を関数名として解釈し呼び出しています。
BEGIN {
for (i = 1; i < 10; i++) {
if (i % 3 == 0) {
var = "gu";
printf("%s -> ", var);
@var();
}
if (i % 3 == 1) {
var = "choki";
printf("%s -> ", var);
@var();
}
if (i % 3 == 2) {
var = "pa";
printf("%s -> ", var);
@var();
}
}
}
function gu() {
print "pa";
}
function choki() {
print "gu";
}
function pa() {
print "choki";
}
このプログラムをindirect1.awkとして実行してみましょう。
$ gawk -f indirect1.awk choki -> gu pa -> choki gu -> pa choki -> gu pa -> choki gu -> pa choki -> gu pa -> choki gu -> pa
ご覧のとおり、変数varの値がguであるときにはgu関数、chokiであるときにはchoki関数、paであるときにはpa関数が呼び出されています。
もう1つ例を挙げましょう。以下のように、行の最後にsumまたはavgという文字列が記述されたテキストがあり、その文字列が出現したタイミングで集計(合計または平均)を行いたいとします。
$ echo -e "1 2 sum\n3 4 avg" 1 2 sum 3 4 avg
そのために、以下のプログラムをindirext2.awkとして保存します。
{
var = $NF;
print @var($1, $2);
}
function sum(n1, n2) {
return n1 + n2;
}
function avg(n1, n2) {
return (n1 + n2) / 2;
}
先ほどのテキストを標準入力で読み込ませる形で、実行してみましょう。
$ echo -e "1 2 sum\n3 4 avg" |\ > gawk -f indirect2.awk 3 3.5
行末の文字列に応じた関数を呼び出すことができます。しかし、「普通に1行野郎編注1で書ける」という声が聞こえてきそうです。
$ echo -e "1 2 sum\n3 4 avg" |\
> awk '{print ($NF == "sum") ? $1+$2 : ($1+$2)/2}'
3
3.5
上記の1行野郎は、行末がsumだったら合計して、それ以外なら平均するという条件演算子で記述した例です。このようにgawkのパッケージに含まれるinfoファイルにはQuick Sortなどいくつかの例が掲載されていますが、使いどころは難しいようです。
編注1:ワンライナーのこと。1行で必要な処理を書き切る。
今回のまとめ
ここまでの3回で、gawkの主な機能は紹介し終えました。次回で最後となりますが、gawkでさらに拡張機能を使う場合の話をします。例えば、最新のgawkは、MPFRがインストールされている環境でビルドした場合、MPFRで任意精度の演算が可能になります。また、準備されている拡張を用いればforkすることも可能で、AWKで並列処理を行うこともできます。お楽しみに。
『シェルスクリプトマガジン 2015 January(Vol.21)』は、ただいま絶賛発売中!

『シェルスクリプトマガジン』(毎月25日発売)は日本で唯一のシェルスクリプト総合誌。最新号 2015 Juanuary(Vol.21)の特集は「こんなシェルスクリプト、書いちゃダメだ。」漢のUNIXでお馴染みの後藤大地さんが、ダメシェルスクリプトを斬ります! 新連載は、クラウディアさんこと戸倉彩さんのコミケ紹介記事「戸倉彩の2.5次元の世界」。冬コミ前にコミケ知識を勉強しましょう!「TechLION再録」では、9月に開催されたvol.18を収録。「未来のライフスタイルとテクノロジー」をテーマに熱いトークが繰り広げられました。本連載「GNU AWK処方箋」の第4回はなんと最終回……! 従来のAWKではできなかったGNU AWKならではの拡張機能について解説します。他にも「IT美女図鑑」や「Haskellでやってはいかんのか?」など好評連載中です!
► お求めは、Amazon.co.jp、USP研究所のWebサイト、取り扱い書店まで。
シェルスクリプトマガジン 2015 January(Vol.21)
価格:500円+税
仕様:B5版 64ページ オールカラー
発行人:ユニバーサル・シェル・プログラミング研究所
