移動先 先頭, , , 末尾 セクション, 目次.

パターン

awkでのパターンはルールの実行を制御している。ルールは現在の入力レコー ドがその(ルールの持つ)パターンにマッチしたときに実行される。この章では、パ ターンの書き方について述べる。

パターンの種類

ここでは、awkがサポートしているパターンについて述べる。

/regular expression/
正規表現はパターンのようなものであり、入力レコードのテキストに一致したとき マッチする。 (セクション パターンとしての正規表現を参照.)
expression
一つの式。数値に変換して0でない、もしくは空でない文字列のときに マッチする (セクション パターンとしての式を参照.)
pat1, pat2
カンマで区切られた二つのパターンはレコードの範囲を指定する (セクション パターンを使ったレコード範囲の特定を参照.)
BEGIN
END
前処理、もしくは後処理をするための特別なパターン。 (セクション スペシャルパターンBEGINENDを参照.)
null
空のパターンはすべての入力レコードにマッチする (セクション 空パターンを参照.)

パターンとしての正規表現

正規表現は文字列のクラスを記述する手段である。正規表現はスラッシュ (`/')に囲まれていて属するクラスが、入力レコードと比較されるawk のパターンである。

最も単純な正規表現は文字、数字の並びである。そのような正規表現はその文字の並 びそのものにマッチする。例えば`foo'という正規表現は`foo'という部分 がある文字列にマッチする。従って、/foo/というパターンは`foo'とい う部分がある入力レコードにマッチする。ほかの正規表現は、文字列のより複雑なク ラスを特定する。

正規表現の使い方

正規表現はスラッシュに囲まれたパターンとして使う事ができる。さらに正規表現は 各レコードのテキスト全体とマッチする (通常は、マッチするテキストの一部分だけ が必要である)。例えば次の例は、レコードのどこでもいいから`foo'という並 びがあればそのレコードの二番目のフィールドを出力する。

awk '/foo/ { print $2 }' BBS-list

正規表現は比較式の中で使う事ができる。文字列をマッチするかどうかテストするこ とができるが、そのとき入力レコード全体が必要というわけではない。そのような比 較式はパターンとして、あるいは if文, while文, for文, do文で使う事ができる。

exp ~ /regexp/
これはexpという式(文字列である)が、regexpとマッチすると真の値 をとる。次の例は、すべての入力レコードの中で大文字の`J'を最初のフィール ドに含むものにマッチし、そういうレコードを選択する。
awk '$1 ~ /J/' inventory-shipped
この動作は次のものと同じである。
awk '{ if ($1 ~ /J/) print }' inventory-shipped
exp !~ /regexp/
これはexpという式(文字列である)が、regexpとマッチしないと真の 値をとる。次の例は、すべての入力レコードの中で大文字の`J'を最初のフィー ルドに含まないものにマッチし、そういうレコードを選択する。
awk '$1 !~ /J/' inventory-shipped

`~' 演算子や `!~' 演算子の右辺には固定正規表現(例え ばスラッシュの間にあるキャラクタの並び)が必須というわけではなく、 なんらかの式であってもよい。そういった式は評価されて、 必要があれば文字列に変換される。その結果の文字列は正規表現として扱わ れる。このように計算された正規表現は動的正規表現と呼ばれる。例を挙げよ う。

identifier_regexp = "[A-Za-z_][A-Za-z_0-9]+"
$0 ~ identifier_regexp

identifier_regexpawkの変数名を表すような正規表現をセットし、 入力レコードに対してこの正規表現がマッチするかどうかテストを行う。

正規表現演算子

以下に挙げる正規表現演算子、あるいはメタキャラクタと呼ばれるキャ ラクタを使って、正規表現を繋げることができ、それによって正規表現の記述力を向 上させ、融通性を増す。

以下はメタキャラクタの一覧である。ここに挙げられていないキャラクタはそれ自身 を表現するキャラクタである。

^
文字列の先頭、もしくは行の先頭にマッチする。たとえば、
^@chapter
これは文字列の先頭にある`@chapter'とマッチする。 (Texinfoのソースファイル中の章の始まりの印としてよく使われる)
$
`^'と似ているが、文字列あるいは行の末尾にマッチする。たとえば、
p$
これは`p'で終わるレコードにマッチする。
.
改行を除くある一文字にマッチする。たとえば、
.P
これは文字列中で、`P'が続くキャラクタにマッチする。連結を使うことにより、 間に任意のキャラクタを挟む`U'で始まり`A'で終わる文字の並びにマッチ する`U.A'の様な正規表現を作ることができる。
[...]
これはキャラクタセットと呼ばれ、ブラケットに挟まれた中にあるキャラクタ のどれか一つとマッチする。たとえば、
[MVX]
これは文字列中の`M', `V', `X'のいずれかとマッチする。 キャラクタの範囲は、範囲の始まりと終わりの間にあるハイフンを用いて示される。 これはブラケットの中になければならない。例を挙げると、
[0-9]
これは数字のどれかとマッチする。 キャラクタセットの中で、`\', `]', `-', `^'はその前に `\'を置く。例を挙げよう。
[d\]]
これは`d'`]'とマッチする。 この`\'の意味は他のawk処理系と同じであり、 POSIX のコマンド言語、標準ユーティリティとも一致する。 awkでの正規表 現はPOSIX の規定した拡張正規表現(EREs)のスーパーセットである。 POSIX EREsは 伝統的なegrep ユーティリティで使用できる正規表現がベースになっている。 egrepのシンタクスでは、バックスラッシュはブラケットに囲まれた中では文 法的に特別なものではない。このことは、キャラクタセットのメンバとして `]', `-', `^'といったキャラクタを使うときには、特殊なトリック を使わなければならないということを意味している。 egrepのシンタクスでは、`-'を範囲指定のキャラクタではなく、単なる `-'にマッチさせる為には`---'と書かなければならない。あるいは `-'をキャラクタセットの先頭か末尾に置いてもよい。 `^'にマッチさせ る為には、それをキャラクタセットの先頭以外に置かなければならない。 `]' にマッチさせる為にはキャラクタセットの先頭にそれを置かなければならない。例を 挙げよう。
[]d^]
これは`]'`d'`^'にマッチする。
[^ ...]
これはキャラクタセットの補集合である。 `['に続く最初のキャラクタは `^'でなければならない。これはブラケットの中にあるキャラクタではないキャ ラクタ(もしくは改行)にマッチする。例えば、
[^0-9]
これは数字以外のキャラクタにマッチする。
|
これは選択演算子であり、選択を特定する為に使われる。例えば、
^P|[0-9]
これは`^P'`[0-9]'のどちらかにマッチする文字列にマッチする。言い 換えるとこのパターンは、`P'で始まるか数字が含まれている文字列にマッチす る。 この選択は演算子の左右のそれぞれで可能な限り大きな正規表現に適用される。
(...)
括弧は正規表現を(算術式のときと同じ様にグループとする為に使用される。これ は選択演算子`|'を含んだ正規表現を連結する為に使う事ができる。
*
このシンボルはその前に置かれている正規表現の0回以上のくり返しにマッチする。 例えば
ph*
ここで`*'はその前にある`h'に適用され、一文字の`p'に続いて任意 の数の`h'がある文字列にマッチする。これは`p'だけで`h'が一個も ないようなパターンにもマッチする。 `*'のくり返しは、可能なかぎり最も小さな式が採用される。 (もしより大きな 式を繰り返したいのならば括弧を使えばよい) そしてその式は可能な限り大きなくり 返しを見つけだす。例えば、
awk '/\(c[ad][ad]*r x\)/ { print }' sample
これは入力レコードの中で `(car x)', `(cdr x)', `(cadr x)'など の文字列があるレコードを全て出力する。
+
このシンボルは`*'と似てはいるが、前に置かれている式が少なくとも一つはな ければならない。
wh+y
これは`wh*y'にマッチするすべての文字列の中で `why'`whhy' にはマッチするが、`wy'にはマッチしない。この演算子を使って、先程の `*' を使った例を単純に書き直せる。
awk '/\(c[ad]+r x\)/ { print }' sample
?
このシンボルは`*'と似ているが、式の高々一回のくり返しにしかマッチしない。 例えば
fe?d
これは`fed'`fd'にはマッチするがそれ以外にはマッチしない。
\
これはキャラクタの特別な意味を打ち消すのに使われる。例えば
\$
これは`$'というキャラクタにマッチする。 文字列定数 (セクション 定数式を参照) で使われたエスケープシー ケンスは正規表現として正当であり、それらは`\'が前に置かれる。

正規表現中では`*', `+', `?'といった演算子は最も高い優先順位を 持ち、続いて連結、最後が`|'である。算術式と同じ様に、括弧は 演算子同士の結びつきをどのようにするのかを変更できる。

大小文字を意識した照合

大小文字は通常正規表現の中では通常のキャラクタマッチのとき(メタキャラクタを除 く)でも、キャラクタセットの内側のときでも区別される。したがって、正規表現中 の`w'は小文字の`w'とだけマッチして大文字の`W'とはマッチしない。

大小文字を無視してマッチを行う最も単純な方法はキャラクタセットを使って `[Ww]'の様にすることである。しかし、これは正規表現を読みにくいものとし てしまう。他に取るべき手段としては二つある。

プログラム中の任意の点で大小文字に関係なくマッチングを行うための手段の一つは、 データをtolowertoupperという組み込みの文字列処理関数(まだ これらの関数の説明はされていない)を使用して、大文字、小文字どちらかに揃えて しまうというものである。(tolower 及びtoupperの詳しい説明は、 セクション 組込みの文字列操作関数を参照). たとえば

tolower($1) ~ /foo/  { ... }

この例ではマッチングを行なう前に、最初のフィールドを小文字に変換している。

別の手段として、変数IGNORECASEにゼロでない値をセットする。というもの がある。IGNORECASEがゼロでないとき、全ての正規表現演算は大小文字を無 視する。 IGNORECASEの値を変更すると、あなたの書いたプログラムの大小文 字の扱い方を実行時に動的にコントロールできる。デフォルトでは IGNORECASEは(他のほとんどの変数と同じように) ゼロに初期化されているの で、大小文字は区別される。

x = "aB"
if (x ~ /ab/) ...   # これは失敗する

IGNORECASE = 1
if (x ~ /ab/) ...   # これは成功する

通常は、IGNORECASEを使ってあるルールでは大小文字を無視するようにし、 別のルールでは大小文字を区別するようにするということはできない。なぜなら、 IGNORECASEを特定のパターンのためにセットすることができないからである。 このような動作を行なうためには、キャラクタセットかtolowerを使わなけれ ばならない。しかし、全てのルールにおいて、大小文字の区別をする、しないを動的 に変更するというのであれば、そのためにIGNORECASEのオン・オフを切り替 えて行なうことはできる。

IGNORECASEをコマンドラインやBEGINルール中でセットすることがで きる。 IGNORECASEをコマンドラインでセットすることによってプログラムを 修正することなしに、そのプログラムを大小文字を無視するように動作させることが できる。

gawk が互換モードで動作している場合、IGNORECASE の値は 効果を持たない(セクション awkの起動を参照). 互換モードでは、大小文字は常に区別される。

パターンとしての比較式

比較パターンは二つの文字列あるいは数値の間の、等しさのような 関係をテストする。これは式パターンの特殊なケースである (セクション パターンとしての式を参照)。 比較パターンはCのスーパーセットとなっている関係演算子を使って 記述される。関係演算子は次に挙げるものがある。

x < y
xy未満のときに真。
x <= y
xy以下のときに真。
x > y
xyより大きいときに真。
x >= y
xy以上のときに真。
x == y
xyが等しいときに真。
x != y
xyが等しくないときに真。
x ~ y
xyで表わされる正規表現にマッチするときに真。
x !~ y
xyで表わされる正規表現にマッチしないときに真。

関係演算子のオペランドはその両方ともが数値であれば、数値として比較が行なわれ る。そうでない場合には (文字列に)変換されてから文字列として比較が行なわれる (セクション 文字列と数値の変換を参照). 文字列は初めに最初のキャラクタ同士を比較し、次に二番目のキャラクタを、 というように違いがあるまで比較を続ける。 したがって、"10""9"よりも小さいということになる。 もし、短い文字列が終了するまでが等しい(長さの違う)二つの文字列があった 場合、短い方の文字列は長い方の文字列よりも小さい。とみなされる。したがって "abc""abcd"よりも小さい、ということになる。

演算子`~'`!~'の左に位置するオペランドは、文字列である。右に位 置するオペランドはスラッシュでくくられた (/regexp/のような)正規 表現か、動的正規表現のように文字列としての値をもつなんらかの式である (セクション 正規表現の使い方を参照).

以下に挙げる例は入力レコードの中から、第一フィールドが `foo'であるレコードの第二フィールドを出力する。

awk '$1 == "foo" { print $2 }' BBS-list

次に挙げる例では正規表現のマッチングを使って、第一フィールドに `foo'が 含まれるレコードの第二フィールドを出力する。

awk '$1 ~ "foo" { print $2 }' BBS-list

これはまた、次のようにも記述できる。

awk '$1 ~ /foo/ { print $2 }' BBS-list

論理式とパターン

論理式は異なったパターンを "or" (`||'), "and"(`&&'), "not" (`!') のような論理演算子を使って繋げた式である。入力レコー ドに対する論理式全体のパターンマッチは、各部分の式のマッチングの結果による。

例えば次のコマンドは入力ファイル`BBS-list'中のレコードのうち、 `2400'`foo'の両方を含むものを出力する。

awk '/2400/ && /foo/' BBS-list

次のコマンドは入力ファイル`BBS-list'中のレコードのうち、`2400'`foo'のどちらか、あるいは両方を含むものを出力する。

awk '/2400/ || /foo/' BBS-list

`BBS-list'中のレコードのうち、`foo'含まないものを出力す る。

awk '! /foo/' BBS-list

論理パターンは式パターンの特殊なケースであり、 (セクション パターンとしての式を参照) 論理演算子を使った式である。 セクション 論理式を参照.に論理演算子の詳しい 記述がある。

論理パターンの部分式は固定正規表現や、比較式、あるいはほかの awkの 式であってもかまわない。範囲パターンは式ではないので論理パターンに含めること はできない。同様に、特殊パターンのBEGINENDもまた、入力レコ ードのいずれともマッチせず、式や論理パターンの中に記述することはできない。

パターンとしての式

awkでは、式はawkのパターンとしての値を持っている。式の値が0以 外(数値の場合)か、空文字列(文字列の場合)でない場合パターンがマッチしたこ とになる。

式は新しく入力レコードに対してルールが検査されるごとに再評価される。もし式が $1のようなフィールドを使っていたならばその値は新たな入力レコードのテ キストによって決定する。そうでない場合、式はawkプログラムの実行とは関 係ないところに依存する形となるが、それでも便利であろう。

パターンの比較はこのような場合の特殊な形である。たとえば、$5 == "foo" という式は $5 の値が "foo"と等しいときに1という値を持ち、等し くなければ0という値を持つ。したがって、この式は二つの値が等しいときにパターン がマッチしたということになる。

論理式は式の特殊なケースである。

パターンとしての正規表現もまた式の特殊なケースである。 /foo/はカレン ト入力レコード中に`foo'があれば 1という値を持つ式である。したがって、 /foo/`foo'を含むレコードにマッチするパターンである。

他のawk処理系ははgawkほどにはPOSIXに準拠していない。 具体的には、比較式や論理式は許されるが、そのほかの種類の式は使えない。

パターンを使ったレコード範囲の特定

範囲パターンbegpat, endpatのように、カンマで区切 られた二つのパターンからなる。これは連続した範囲の入力レコードにマッチする。 最初のパターンbegpatは範囲の始まりを制御し、二番目のパターン endpatは範囲の終わりを制御する。たとえば、

awk '$1 == "on", $1 == "off"'

これは`on'/`off'のあるレコードの間にあるレコードを `on'/`off'のあるレコードも含めて出力する。

範囲パターンは入力レコードと比較しbegpatとマッチすることで始まる。レコ ードがbegpatとマッチした時、範囲パターンは真となる。 endpatとマッ チする入力レコードがみつかるまですべての入力レコードはパターンにマッチしたと 扱われる。 endpatとマッチする入力レコードが見付かったとき、範囲パター ンはそれ以降の入力レコードのマッチングの際には偽となる。そして今度はまた入力 レコードとbegpatがマッチするかどうかを検査するのである。

範囲パターンを真にしたり、偽にしたりしたレコードそのものも範囲パターンにマッ チしたと扱われる。もしここでそのようなレコードは扱いたくないというのであれば if文を使って、ルールのアクション部分でそのようなレコードを区別すれば 良い。

範囲の始まりと終わりを同じパターンにすることも可能である。そのような場合、ア クションはマッチしたレコードに対してのみ実行される。

スペシャルパターンBEGINEND

BEGINENDは特殊なパターンである。これらのパターンは入力レコー ドとのマッチングには使用されず、 awkスクリプトのスタートアップやクリー ンアップに使われる。 BEGINルールは一度だけ、最初の入力レコードが読み 込まれる前に実行され、ENDもやはり一度だけ、すべての入力が行なわれた後 で実行される。例を挙げよう。

awk 'BEGIN { print "Analysis of `foo'" }
     /foo/ { ++foobar }
     END   { print "`foo' appears " foobar " times." }' BBS-list

このプログラムは入力ファイル`BBS-list'中のレコードのうち、 `foo'と いう文字列を含むレコードの数を出力する。 BEGINルールでレポートのタイ トルを出力する。ここで、BEGINルールの中で、カウンタに使用する変数 foobarを 0に初期化する必要はない。awkが自動的にそれを行なうか らである (セクション 変数を参照).

二番目のルールで、`foo'を含むレコードを読むたびに変数foobarをイ ンクリメントしている。 ENDルールでは実行終了時のfoobarの値を出 力している。

BEGINENDは範囲を示すためには使えないし、論理演算子と一緒に 使うこともできない(それどころかそれらは他の演算子と一緒に使うこともできない)。

awkプログラムではBEGIN ルールや ENDルールを複数記述する こともできる。そのような複数のルールは、プログラムの先頭から見付かった順に全 てのBEGINルールはスタートアップ時に、全てのENDルールは終了時に 実行される。

複数のBEGINENDはライブラリを記述するのに便利である。ライブ ラリはそれぞれ自分のBEGIN ルールや ENDルールを自分のスタートアッ プやクリーンアップのために持つことができる。気を付けなければならないのは、コ マンドラインに記述されるライブラリの名前の順番によって、(ライブラリ中の) BEGIN ルールや ENDルールの実行される順番が左右されるということ である。したがって、ライブラリファイルで実行される順番に依存するような記述を しないように注意しなければならない。より詳しいライブラリ関数の使い方は セクション awkの起動を参照,を参照のこと

awkプログラムがBEGINルールだけで他のルールを持っていない場合、 BEGINルールの実行をした後でプログラムを終了する(古いバージョンの awkでは入力を続け、ファイルの終端にくるまで入力を無視し続ける)。しか し、同様にENDルールだけがプログラム中にあった場合、入力は行なわれる。 これは ENDルールでNR変数をチェックするようなケースに必要である からである。

BEGIN ルールおよび、 ENDルールはアクション部を持っていなければ ならない。これらのルールにはデフォルトアクションはなく、実行時にカレントレコ ードも存在しないからである。

空パターン

空のパターンは全ての入力レコードにマッチするとみなされる。 たとえば次のプログラムは

awk '{ print $1 }' BBS-list

全てのレコードの第一フィールドを出力する。


移動先 先頭, , , 末尾 セクション, 目次.