AWK
この教科書は、テキスト処理スクリプト言語 AWK の入門用として書かれています。
この言語の名称は、3人の開発者
- アルフレッド・エイホ(Alfred Aho)
- ペーター・ワインバーガ(Peter Weinberger)
- ブライアン・カーニハン(Brian Kernighan)
の3人の頭文字に由来し、「オーク」と発音します。
AWKは、UNIXシステムで最初に開発され、テキスト処理やデータ抽出に広く使用されています。 AWKの基本構文は非常に簡単で、シェルスクリプトのように構造化されています。 AWKは、テキストファイルを処理するための機能が豊富であり、行指向の操作、パターンマッチング、算術演算、条件分岐、ループなどの機能があります。 AWKはまた、C言語の構文に似た独自の言語構造を持ち、ユーザー定義関数や変数などの高度なプログラミング機能もサポートしています。
処理系に触れてみる
[編集]Windows環境用にはGawk for Windowsやgawk 3.1.5 for Windowsなどがあります。UNIX系のOSをご利用の場合、デフォルトでインストールされていることがほとんどです。
まずは何か文字を表示してみましょう。コマンドラインから次のように入力してください。
awk 'BEGIN{ print "hello, world" }'
hello, world と表示されましたね。
少し難しいかも知れませんが、次はAWKが得意とするパターンマッチングに基づくテキストファイルの処理を見てみます。 student.dataという以下のようなファイルがあったとしましょう。
- student.data
Ichiro 85 63 Hanako 82 78 Toyoko 77 62 Yumiko 68 91
これは左から生徒の名前、国語の点数、英語の点数を表記しスペースで区切られています。英語が70点以上の生徒の国語の点数の合計を求めてみましょう。 コマンドラインから以下のように入力します。
awk 'BEGIN{ x = 0 } $3 >= 70 { x = x + $2 } END { print x }' student.data
そうすると、 150 と表示されます。
プログラムの構造
[編集]先ほどのプログラムで何が起こっているのか考えてみましょう。
awk というのは AWK スクリプトを実行するためのコマンドです。スクリプト本体は'
(シングルクオート)で囲まれています。
BEGIN { }
という構造がありますが、これはプログラムを起動した時、最初に実行される部分です。ここで
- としているのは「変数xに0を代入しなさい」という意味です。
x = 0
END { }
という構造は、最後に実行される部分です。ここで
print x
となっているのは「変数xの値を表示しなさい」という意味です。 問題は以下の部分です。
$3 >= 70 { x = x + $2 }
これは以下のような構造になっています。
パターン { アクション }
studento.data 一行ずつ読み込んでいき、読み込んだ行がパターンに該当(マッチ)した場合のみ { } 内のアクションを実行するようになっています。 $3 というのは3番目のフィールド、つまり Ichiro の行で言えば 63 を意味します。同じく $2 といえば2番目のフィールドということになります。肝心の実行内容ですが、「読み込んだ行の3番目のフィールドが70以上だったら(英語が70点以上だったら)xに2番目のフィールドの値を加えなさい」ということです。
AWKはファイルを読み込ませると自動的に1行ずつ読み込んでいくようになっています。このとき、英語が70点以上なのは Hanako と Yumiko で、この時だけ国語の点をxに加えるのですから、82点+68点で、合計150点になるというわけです。
パターン
[編集]BEGINとEND
[編集]BEGINとENDではじまるブロックはプログラムの最初と最後に必ず実行されます。BEGINは入力を読み込む前(「処理系に触れてみる」で言うとstudent.dataを読み込む前)に実行される部分です。また、ENDは全ての入力が終わった後に実行されます。
複数のBEGINやENDがある場合、それらが記述されている順に実行されます。必ずそうする必要はありませんが、習慣的にBEGINは最初に、ENDは最後に記述します。
アクション
[編集]ユーザ定義関数
[編集]出力
[編集]単純に読み込んだ行を全て出力するだけなら
{ print }
あるいは
//
とすれば済みます。
$ の後に数字をつけると特定のフィールドを指定できることをすでに学びましたが、皆さん思えていますか? ではもし $0 としたら、これは何を意味するのでしょうか? 実は、レコード全体(行全体)のことを表します。ですから、
{ print $0 }
としても読み込んだ行を全て出力します。