既に説明したように、それぞれのawkの文は
アクションを伴ったパターンから構成されている。
この章ではパターンとアクションをどのように作成するかを
説明する。
awkのパターンはルールの実行を制御する。あるルールはそのパターンと
カレント入力レコードがマッチしたときに実行される。このセクションではパタ
ーンの記述の仕方を説明する。
以下にawkでサポートしているパターンをまとめる。
/regular expression/
expression
BEGIN
END
awkプログラムで、前処理もしくは後処理をするための特別なパターン
(セクション 特殊パターンBEGINとENDを参照.)
empty
これまでのサンプルの中で、正規表現をパターンとして使ってきた。 この種類のパターンは、 あるルールのパターン部分にある 単純な正規表現定数である。それは`$0 ~ /pattern/'. と同じ意味を持つ。これは入力レコードが正規表現にマッチしたとき、 (入力レコードに)マッチするパターンである。 例を挙げよう。
/foo|bar|baz/ { buzzwords++ }
END { print buzzwords, "buzzwords seen" }
awkの任意の式は、そのままawkの正しいawkのパターンである。
そして、式の値が非0(数値の場合)でもなく、空でない文字列
(文字列の場合)の場合には、パターンマッチするということになる。
式は、ルールが新たな入力レコードに対してテストされる度に再評価が
行われる。もしここで式が$1のようなフィールドを使っていた
のならば、その値は新たな入力レコードテキストによるものである。
それ以外のものはawkプログラムの実行時の動作にのみ
依存する。
パターンとして使われている式は、一般的に セクション Variable Typing and Comparison Expressionsを参照. にある比較演算子を使った比較式をパターンとして使っている。
(セクション 動的正規表現を使うを参照).
正規表現マッチング(と非マッチング)は非常に一般的な式である。
`~'演算子や`!~'演算子の左オペランドは
文字列であり、右オペランドはスラッシュで囲まれた
(/regexp/のような)正規表現定数か
動的正規表現として扱われる文字列値を持つ任意の式である。
`~'演算子や`!~'演算子の左オペランドは文字列であり、
右オペランドは(/regexp/)のように
スラッシュで囲まれた正規表現定数か、動的正規表現として使用される
文字列としての値を持つ任意の式である
(セクション 動的正規表現を使うを参照)。
次に挙げる例は、第一フィールドが`foo'である入力レコードの第二フィ ールドを出力する。
$ awk '$1 == "foo" { print $2 }' BBS-list
(これはなにも出力しない。なぜなら、"foo"という名前のBBSは ないからだ。) 対照的に、次の例では 第一フィールドに`foo'を含むレコードを出力するものである。
$ awk '$1 ~ /foo/ { print $2 }' BBS-list
-| 555-1234
-| 555-6699
-| 555-6480
-| 555-2127
ブール式もまた同様にパターンとして使われている。パターンが入力レ コードにマッチするかどうかを、部分式がマッチするかどうかで判定す る。
例を挙げると、次のコマンドは `2400'と`foo'の両方を含む`BBS-list'中の レコード全てを出力する。
$ awk '/2400/ && /foo/' BBS-list -| fooey 555-1234 2400/1200/300 B
次の例は`2400' か `foo'のいずれか(両方でもよい)を含む `BBS-list'中のすべてのレコードをすべて出力する。
$ awk '/2400/ || /foo/' BBS-list -| alpo-net 555-3412 2400/1200/300 A -| bites 555-1675 2400/1200/300 A -| fooey 555-1234 2400/1200/300 B -| foot 555-6699 1200/300 B -| macfoo 555-6480 1200/300 A -| sdace 555-3430 2400/1200/300 A -| sabafoo 555-2127 1200/300 C
次の例では、`foo'という文字列を含まない`BBS-list'の レコードを全て出力する。
$ awk '! /foo/' BBS-list -| aardvark 555-5553 1200/300 B -| alpo-net 555-3412 2400/1200/300 A -| barfly 555-7685 1200/300 A -| bites 555-1675 2400/1200/300 A -| camelot 555-0542 300 C -| core 555-2912 1200/300 C -| sdace 555-3430 2400/1200/300 A
パターン中にあるブール演算子の部分式は、正規表現定数や、比較、あるいは任
意のawk式であってよい。範囲パターンは式ではなく、論理パターンの内
側に置くことはできない。同様に、あらゆる入力レコードとはマッチしないスペ
シャルパターンBEGINやENDも式ではなく、論理パターンの内側に
置くことはできない。
パターンとしての正規表現定数も同様に expression patternの特殊な
ケースである。/foo/は、
`foo'がカレント入力レコードにあるときに1という値を持つ式である。
したがって、/foo/は`foo/'を含むレコードにマッチする
パターンである。
範囲パターン(range pattern)はbegpat, endpatの
ように、カンマで区切られた二つのパターンからなる。これは連続した範囲の
入力レコードにマッチする。最初のパターンbegpatは範囲の始まりを制
御し、二番目のパターンendpatは範囲の終わりを制御する。たとえば
awk '$1 == "on", $1 == "off"'
これは`on'/`off'のあるレコードの間にあるレコードを `on'/`off'のあるレコードも含めて出力する。
範囲パターンは入力レコードと比較しbegpatとマッチすることで始まる。レコ ードがbegpatとマッチした時、範囲パターンは真となる。 endpatとマッ チする入力レコードがみつかるまですべての入力レコードはパターンにマッチしたと 扱われる。 endpatとマッチする入力レコードが見付かったとき、範囲パター ンはそれ以降の入力レコードのマッチングの際には偽となる。そして今度はまた入力 レコードとbegpatがマッチするかどうかを検査するのである。
範囲パターンを真にしたり、偽にしたりしたレコードそのものも範囲パターンにマッ
チしたと扱われる。もしここでそのようなレコードは扱いたくないというのであれば
if文を使って、ルールのアクション部分でそのようなレコードを区別すれば
良い。
範囲の始まりと終わりを同じパターンにすることも可能である。そのような場合、ア クションはマッチしたレコードに対してのみ実行される。
例えば、二つの目印(ここでは`%'としよう)に挟まれている
テキストを無視したいと考えたとしよう。
これを区切りのテキストを記述した範囲パターンとnext文
(まだ説明していない。セクション The next Statementを参照)
を組み合わせて、awkに処理をスキップさせることを
例えば以下ののプログラムのようにして試みるかもしれない。
/^%$/,/^%$/ { next }
{ print }
このプログラムは、`%'だけがある最初の行で 範囲の開始と終了を行ってしまうので失敗してしまう。 これを行うには、フラグを使用して次のようなプログラムを 書かなければならない。
/^%$/ { skip = ! skip; next }
skip == 1 { next } # `skip' がセットされていたらその行を飛ばす
範囲パターンの中では、`,'はすべての演算子の中で もっとも優先順位の低い(最後に評価が行われる)演算子であることに 注意すること。したがって、次の例のようなプログラムでは 別の単純なテストと範囲パターンが組み合わされて扱われてしまう。
echo Yes | awk '/1/,/2/ || /Yes/'
このプログラムの作者は`(/1/,/2/) || /Yes/'のつもりだった。
しかし、awkはこれを`/1/, (/2/ || /Yes/)'のように
解釈する。これは変更したり、対処したりすることはできない。
範囲パターンは他のパターンと組み合わせないこと。
BEGINとEND
BEGINとENDは特殊なパターンである。
これらは入力レコードにマッチするためには使われなず、
awkスクリプトのスタートアップやクリーンアップに
使われる。
BEGINルールは一度だけ、最初の入力レコードが読み込まれる前に実行され、
ENDもやはり一度だけ、すべての入力が行なわれた後で実行される。
例を挙げよう。
$ awk '
> BEGIN { print "Analysis of \"foo\"" }
> /foo/ { ++n }
> END { print "\"foo\" appears " n " times." }' BBS-list
-| Analysis of "foo"
-| "foo" appears 4 times.
このプログラムは入力ファイル`BBS-list'中のレコードのうち、 `foo'と
いう文字列を含むレコードの数を出力する。 BEGINルールでレポートのタイ
トルを出力する。ここで、BEGINルールの中で、カウンタに使用する変数
foobarを 0に初期化する必要はない。awkが自動的にそれを行なうか
らである (セクション Variablesを参照).
二番目のルールで、`foo'を含むレコードを読むたびに変数foobarをイ
ンクリメントしている。 ENDルールでは実行終了時のfoobarの値を出
力している。
BEGIN と ENDは範囲を示すためには使えないし、論理演算子と一緒に
使うこともできない(それどころかそれらは他の演算子と一緒に使うこともできない)。
awkプログラムではBEGIN ルールや ENDルールを複数記述
することもできる。そのような複数のルールは、プログラムの先頭から見付かっ
た順に全てのBEGINルールはスタートアップ時に、全てのENDルー
ルは終了時に実行される。この機能は、awkの1987年のバージョンで付け
加えられた。また、これはPOSIXの標準にも含まれている。オリジナル(1978年)
のawkでは、BEGINルールはプログラムの先頭に、ENDルー
ルはプログラムの最後に置かれていて、それぞれプログラム中に一つだけあるこ
とを要求していた。今やこうする必要はないのだが、このようにすることはプロ
グラムの構成を改良し、可読性を向上させるアイデアではある。
複数のBEGIN や ENDはライブラリを記述するのに便利である。ラ
イブラリはそれぞれ自分のBEGIN ルールや ENDルールを自分のス
タートアップやクリーンアップのために持つことができる。気を付けなければな
らないのは、コマンドラインに記述されるライブラリの名前の順番によって、
(ライブラリ中の)BEGIN ルールや ENDルールの実行される順番
が左右されるということである。したがって、ライブラリファイルで実行される
順番に依存するような記述をしないように注意しなければならない。より詳しい
ライブラリ関数の使い方は
セクション コマンドラインオプションを参照、
便利なライブラリ関数は
セクション awkの関数ライブラリを参照。
BEGINルールだけで、他のルールを一切持っていないawkプログラ
ムは、BEGINルールを実行した後でプログラムの実行を終了する(オリジ
ナルバージョンのawkでは、ファイルの終端まで入力ファイルを読み込み、
呼んだ内容を無視していた)。しかし、ENDルールがあった場合には他の
ルールがなかったとしても、入力の読み込みを行う。これはENDルールで
FNR や NRという変数をチェックする場合に必要だからである
(d.c.)。
BEGIN ルールおよび、 ENDルールはアクション部を持っていなければ
ならない。これらのルールにはデフォルトアクションはなく、実行時にカレントレコ
ードも存在しないからである。
BEGIN and END Rules
BEGINルールやENDルールから入出力を行ったときに
生じる幾つかの(時として微妙な)事柄がある。
第一の点は、BEGINルール中で$0の値を扱うことに関してのもの
である。BEGINルールは何等かの入力が行われる前に実行されるので、入
力レコードも、フィールドもBEGINルール実行時には存在していない。
$0やフィールドを参照すると、その結果は空文字列かゼロ(これは文脈によ
る)のいずれかになる。$0に本当の値を格納する手段としては、変数指定
なしでgetline(セクション getlineを使った入力を参照)
コマンドを使うというものがある。もう一つのやり方として、単に値を代入すると
いう手段もある。
第二の点は第一のものに似ているが、違う方向のものである。ENDルール
の内側では、$0やNFは伝統的に大部分の処理系においてEND
ルール中の$0やNFは未定義だった。POSIXの標準は、
NFがENDルール中で使用可能であることを規定しており、その内容
は最後の入力レコードのフィールド数を保持しているように定められている。恐
らくは見落としのために、この標準では論理上そう考えるべきであるにもかから
わず、$0を同様に保存するということに言及していない。事実、
gawkはENDルールのために、$0の値を保存している。
第三の点は、先の二つに関連するものである。BEGINルールや
ENDルール中で`print'が意味するところはなんだろうか?
それは通常と同じく、`print $0'である。
$0が空文字列であれば、空行が印刷される。
長い間awkプログラマは、BEGINルールやENDルールの
中で、$0が空文字列であることを当てにして
`print ""'の意味で`print'を使っていた。
BEGINルールでこれを使っていたらそれはやめたほうが良い。
少なくともgawkを使っているときには、これを
ENDルールで使うのは悪いアイデアである。
これは良くないスタイルであり、空の行を出力したいのならば
それを明確に記述すべきである。
空のパターン(つまり、パターンがないということ)はすべての入力レコー ドとマッチするように扱われる。例えば次のプログラムでは、
awk '{ print $1 }' BBS-list
すべてのレコードの第一フィールドを出力する。
awkのプログラムはルールと関数定義などの並びの集まりである
(関数はこの後で説明する。
セクション ユーザー定義関数を参照.)。
ルールはパターンとアクション、もしくはそのどちらか一方からなる。
アクションの目的はawkに対してパターンがマッチしたときに何を行な
うかを示すということである。したがって、完全な形のプログラムは次のようなもの
である。
[pattern] [{ action }]
[pattern] [{ action }]
...
function name(args) { ... }
...
アクションは、カーリーブレース(`{' and `}')に囲まれたひとつ以上
のawkの文から構成される。それぞれの文は一つのことをする。文は改行かセ
ミコロンで区切られている。
たとえアクション部がたった一つの文だけであったり、文がなかったとしても、アク ション部をカーリーブレースで囲まなければならない。しかし、アクション部を省略 するのであればカーリーブレースも一緒に省略することができる(省略されたアクショ ンは`{ print $0 }'と同じ働きをする)。
/foo/ { } # fooにマッチして、なにもしない - 空のアクション
/foo/ # fooにマッチしてレコードを出力 - 省略されたアクション
以下に挙げるものはawkがサポートする文の種類である。
awkプログラムの制御フローを特定する制御文。 awk言語ではCと同じ
ような(if, for, while, など) 制御文と、幾つかの特殊な制
御文がある
(セクション アクション中の制御文を参照).
if文やwhile文、
do文、for文で扱うために使用する。
getlineコマンド
(セクション getlineを使った入力を参照)や
next文
(セクション The next Statementを参照)、
nextfile 文
(セクション The nextfile Statementを参照).
を使った入力文。
print や printfといった出力文。
セクション 出力を行うを参照.
delete Statementを参照.
次の章は制御文を詳しく説明している。