既に説明したように、それぞれの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を参照.
次の章は制御文を詳しく説明している。