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

アクション中の制御文

全ての制御文はifwhileといった特殊なキーワードで始まり、単純 な式とは区別される。 awkの制御文の大部分はCのそれと同じである。

多くの制御文は他の文を構成要素に持っている。例えば if文は実行されたり されなかったりする別の文を持ち、そのような文はbodyと呼ばれる。もし body に二つ以上の文を含めたいのであれば、カーリーブレースを使うことによりセ ミコロンや、改行で区切られた文の集まりをひとつの複合文としてまとめることがで きる。

if

if-else文はawkの意志決定をする文であり、 次のような形をしている。

if (condition) then-body [else else-body]

conditionは文の残りの部分を制御する式である。もしconditionが真で あればthen-bodyが実行され、偽であればelse-bodyが実行される (else以降がある場合)。文のelse部分は省略可能である。 conditionはその値が0か空文字列であれば偽として見なされ、それ以外の値は 真であると見なされる。

例を挙げよう。

if (x % 2 == 0)
    print "x は偶数"
else
    print "x は奇数"

この例では、x % 2 == 0という式が真である(つまり、 xが 2で割り 切れる値である)ときに、最初のprint文を実行し、そうでない(2で割り切 れない)ときに二番目のprint文を実行する。

elsethen-bodyと同じ行にあり、then-bodyが複合文でない (カーリーブレースで囲まれていない)とき、セミコロンで then-bodyelseを区切らなければならない。この規則により、先の例は次のように書け る。

awk '{ if (x % 2 == 0) print "x は偶数"; else
        print "x は奇数" }'

ここで`;'を付け忘れてしまった場合、awkは構文解析を続けられなくな り、シンタックスエラーとなる。

我々は実際にはこの例のような書き方はしない。なぜなら人が読むときに、行の最初に なければelseを読み落す可能性があるからである。

while

プログラミングにおいては、ループとはプログラムの一部分を二回以上実行す る(少なくともその可能性がある)ことである。

while文はawkの中で最も単純な繰り返しの文である。条件が真である 間、文を繰り返し実行する。 while文は次のような形をしている。

while (condition)
  body

ここでbodyは我々が繰り返しのbodyと呼んでいる文であり、 conditionはどれ位の間繰り返しを続けるかを制御する式である。

while文で最初に行なわれるのはconditionの検査である。もし conditionが真であればbodyの文を実行する。(conditionは値が 0でもなく、空文字列でもなければ真である) bodyを実行したあとで、再び conditionをテストし、それがまだ真であればbodyが実行される。この プロセスはconditionが真でなくなるまで繰りかえされる。 condition が最初に偽であった場合、ループの本体は決して実行されない。

次の例は、入力行の最初の三つのフィールドを一行にフィールドひとつという形で出 力する。

awk '{ i = 1
       while (i <= 3) {
           print $i
           i++
       }
}'

ここではループの本体は二つの文をカーリーブレースでくくった複合文である。

ループは次のように動作する。最初にiの値を1にセットし、その後に whileiが3以下であるかどうかを検査する。 iがある数字で あったとき、i番目のフィールドが出力される。i++によってi の値が増やされて、ループが繰り返される。ループはiが4になったときに終 了する。

例を見るとわかる通り、改行は条件と本体の間になくても良い。しかし、本体が複合 文か非常に単純なものでない限り、改行を入れることによってプログラムの見通しが よくなる。複合文の初めにある左カーリーブレースのあとの改行はなくても構わない が、プログラムが読み辛いものになるだろう。

do-while

doループはwhile文のバリエーションである。 doループでは bodyが一回実行され、その後conditionが真である間bodyが繰り 返される。 doループは次のような形である。

do
  body
while (condition)

開始時にconditionが偽であったとしても、bodyが少なくとも一回 (bodyの実行によってconditionが真にならない限りはそれきり)は 実行され、対応するwhile文とは対照的である。

while (condition)
  body

この文ではconditionが開始時に偽であった場合、bodyは実行されない。

do文の例を挙げよう。

awk '{ i = 1
       do {
          print $0
          i++
       } while (i <= 10)
}'

この例ではレコードが入力されるごとに10回出力を行なう。これは通常はwhile で行なうようなものであるから現実的な例ではない。しかし、それは実際の状況を 反映している。なぜなら本当にdo文を使うことというのはまれであるからで ある。

for

for文はループの繰り返しを数えるのに便利である。一般的なfor文は 次のような形をしている。

for (initialization; condition; increment)
  body

この文はinitializationの実行で始まり、続いてconditionが真である 間繰り返してbodyincrementが実行される。典型的な例では、 initializationで変数に0や1をセットするということが行なわれ、 incrementでその変数に1加えられる。そしてcondition ではそれが繰り 返しの回数を決める数と比較される。

for文の例を挙げてみよう。

awk '{ for (i = 1; i <= 3; i++)
          print $i
}'

この例では、入力レコードごとに最初の三つのフィールドを一行にひとつのフィール ドで出力する。

for文の中ではbodyは文であるが、initializationconditionincrementは式である。 initialization部では x = y = 0のような同じ初期値を全ての変数に代入する多重代入文は書くこと ができない。(しかし、forループの前に、変数の初期化を追加することはで きる)

このことは変数のインクリメントを行なうincrement部でも同じことである。 もしインクリメントする文を追加したいのなら、ループの終わりに文を分けて書かな ければならない。 Cのカンマ演算子を使ったCの複合式は、そのような文脈であって も使うことができる。が、awkではサポートされていない。

ほとんどの場合はincrementは、先に挙げた例のように増減式(increment expression)である。しかし、常にそうである訳ではない。式であれば何でも記述す ることができる。たとえば、次の例では2の1乗から100乗まで出力する。

for (i = 1; i <= 100; i *= 2)
  print i

forに続くカッコの中にある三つの式はいずれも必要がなければ省略すること ができる。したがって、`for (;x > 0;)'`while (x > 0)'と 等価である。conditionが省略された場合にはconditionが常に真 であると解釈され、無限ループ (終了せずに繰り返しを続けるループ)とな る。

forループの大部分は、次のようなwhileループの省略形である。

initialization
while (condition) {
  body
  increment
}

唯一の例外は、ループの中で continue文 (セクション continueを参照) が使われているときである。このやり方でfor 文を whileに書き換え るときはループの中のcontinue文の効果を変えることができる。

次にfor文の特殊なバージョンの例を挙げよう。これは配列の添え字をすべて 繰り返す。セクション awkにおける配列を参照.

for (i in array)
    do something with array[i]

awk言語はwhile文に加えて、for文を持っている。なぜなら、 forループはしばしば記述したよりも少ないタイプ量でより自然な考え方であ るからである。繰り返しの回数を数えるループはよくあるループであり、 forを使うことによってループの内側で数えあげるよりもループの数えあげ部 を考えた方が簡単である。

次のセクションではより複雑なforループの例を挙げる。

break

break文は最も内側のforか、while、または do-whileループから脱出する。以下に挙げる例はある整数の最も小さ い約数を探し、素数であるか確認する。

awk '# 指定された数値の最小の約数を探す
     { num = $1
       for (div = 2; div*div <= num; div++)
         if (num % div == 0)
           break
       if (num % div == 0)
         printf "%dの最小の約数は%d\n", num, div
       else
         printf "%dは素数\n", num  }'

最初のif文で剰余が0であったとき、awkは即座に forループ からbreaks outする。これはawkはループに続く文に即座に進み、処理 を続けるということである。 (これはexit文が、awkプログラムの実 行を完全に止めてしまう。ということとは異なっている セクション exitを参照.)

次の例は先の例と等価な別のプログラムである。これは、forwhile の条件がifの内側にある breakに置き換えられるということを示して いる。

awk '# 指定された数値の最小の約数を探す
     { num = $1
       for (div = 2; ; div++) {
         if (num % div == 0) {
           printf "%dの最小の約数は%d\n", num, div
           break
         }
         if (div*div > num) {
           printf "%dは素数\n", num
           break
         }
       }
}'

continue

continue 文は breakと似ていて、 forwhiledo-whileの中でだけ使われる。 continueはループ本体の残りの部分をスキップし、ループの次の サイクルを直ぐに始めさせる。例を挙げよう。

# "ignore"が含まれていない名前を出力する

# 最初にテキストの各行を保存する
{ names[NR] = $0 }

# 該当するものを出力
END {
   for (x in names) {
       if (names[x] ~ /ignore/)
           continue
       print names[x]
   }
}

もし文字列`ignore'を含むレコードがあると、このプログラムはそのレコード を出力するprint文をスキップし、ループの最初の文に戻る。

次の例はcontinueの現実的な例ではないが、理解しやすいループである。

for (x in names)
  if (names[x] !~ /ignore/)
    print names[x]

forループ中のcontinue文はawkに直接ループ本体の残りの部 分をスキップさせ、for文の increment-expressionを実行させる。例を挙げ よう。

awk 'BEGIN {
     for (x = 0; x <= 20; x++) {
         if (x == 5)
             continue
         printf ("%d ", x)
     }
     print ""
}'

このプログラムは0から20のうち、5を除いたすべての数を出力する。 x++が スキップされないので、xは5のまま止まるということはない。この forループは次に挙げるwhileループとは対照的である。

awk 'BEGIN {
     x = 0
     while (x <= 20) {
         if (x == 5)
             continue
         printf ("%d ", x)
         x++
     }
     print ""
}'

このプログラムはxが一度5になると、永久にループし続ける。

先に書いた通り、continue文はループの外側で使っても何の意味もない。し かしドキュメント化されてはいないが、伝統的なawkの実装では、 ループの外側にあるcontinue文をnext文であるかのように扱 う (セクション nextを参照). デフォルトでは gawkはこの使い方をサポートしている。しかし、 `-W posix'がコマン ドラインで指定された場合には (セクション awkの起動を参照), そのような使い方をエラーとする。なぜなら POSIX 標準では continueはルー プ本体の内側で使わなければならないとされているからである。

next

next文は強制的にawkに対して、即座にカレントレコードの処理を中 断させ、次のレコードの処理に移させる。つまり、その先のルールはカレントレコー ドに対して適用されないということである。同様に、その時点で適用されているルー ルのアクション部の残りもまた実行されない。

これと対照的な効果をもたらすのがgetline関数 (セクション getlineを使った入力を参照)である。 getlineawkに次のレコードを即座に読み込みさせるが、その後の 制御は変更しない。したがって、その時点で実行しているアクションの残りの部分は、 新しい入力レコードに対して行なわれる。

最も高いレベルでは、awkプログラムの実行とは入力レコードを読み、それに 対してそれぞれのルールのパターンを突きあわせるというループである。このループ を本体にルールがある forループのようなものだと考えるならば、 next文は continue文のようなものである。この暗黙のループの本体 の最後までスキップし、 increment(つまり、別のレコードを読む)を実行する。

例えば、あなたのawkプログラムが4つのフィールドからなるレコードに対し てだけ働き、それ以外の入力が与えられたときに失敗したくない。というのであれば、 プログラムの先頭近くのルールで次のように書けば良い。

NF != 4 {
  printf("line %d skipped: doesn't have 4 fields", FNR) > "/dev/stderr"
  next
}

こうすれば、このルール以降のルールに正しくないレコードが渡されることはない。 エラーメッセージはエラーメッセージがそうあるべきであるように、標準エラー出力 に出力している。セクション 標準入出力ストリームを参照.

POSIX の標準に従うと、next文がBEGIN ルールや END ルール 内で使われたときの動作は未定義である。 gawkではそれをシンタックスエラ ーであると見なす。

next文の実行が入力の最後に到達させ、ENDルールに 制御が移った場合それは実行される。 セクション スペシャルパターンBEGINENDを参照.

next file

next file文はnext文と同類である。しかし、カレント入力レコード の処理を中断させるのではなく、next fileawkに対して現在処理 しているデータファイルの処理を中断させるのである。

next file文の実行によって、FILENAMEの内容はコマンドラインの次 のファイルの名前にアップデートされ、FNRは1にリセットされてプログラム の最初のルールから処理が開始されるセクション 組込み変数を参照.。

next file文の実行が入力の最後に到達させ、ENDルールに 制御が移った場合それは実行される。 セクション スペシャルパターンBEGINENDを参照.

next file文はgawkでの拡張機能である。この機能は(少なくとも現 在のところ)他のawkでは使用できない。 next fileが行なう動作を `nextfile.awk'という名前のライブラリファイルの作成することと、このあと の内容でシミュレートできる。 (次のサンプルプログラムではまだ説明していない、 ユーザーが定義する関数を使っている。 詳細は セクション ユーザー定義関数を参照.)

# nextfile -- 現在のファイルの残りのレコードをスキップする関数

# これはawkプログラムの"メイン"以前に読まれるようにする

function nextfile() { _abandon_ = FILENAME; next }

_abandon_ == FILENAME && FNR > 1   { next }
_abandon_ == FILENAME && FNR == 1  { _abandon_ = "" }

このnextfile関数は単純に"プライベート"な変数に (4) 現在のデータファイルの名前をセットし、次のレコードを取り出している。このファ イルはメインの awkプログラムの前に読まれているので関数定義のあとのルー ルはメインプログラムのルールより先に実行される。最初のルールは入力しているファ イルの名前が変わらない間、レコードをスキップし続けるがファイルの最初のレコー ドのときには実行されない。このルールはほぼ十分なものであるが、同じ名前のデー タファイルがコマンドラインで二度指定されていたらどうなるか? このルールでは二 度目のファイルは処理しない。二番目のルールはこのようなケースを救済する。もし、 データファイルの名前がスキップしたものであっても FNRが1であれば、これ は二度目の処理を行なうファイルであり、スキップしてはいけない。

next file文は多くのデータファイルを処理する状況で、データの性質によっ て、ファイル中のレコードの全てを処理する、ということを望まない場合に便利であ る。次のデータファイルに移る手段として、希望しないレコードをスキャンし続けな ければならないだろう(先程書いたように)。 next file文はより能率的にそ れを実行する。

exit

exit文はawkに対して即座に現在処理しているルールの実行をやめさ せ、入力の処理もやめさせる。残った入力は無視される。

exit文がBEGINルールで実行された場合、プログラムは停止し、すべ ての処理も即座に停止して、入力レコードは読まれない。しかし、ENDルール がある場合にはそれが実行される。 (セクション スペシャルパターンBEGINENDを参照).

exitENDルールで使われた場合には プログラムは即座に停止する。

通常のルールの中で(つまりBEGIN ルールでも ENDルールでもない) exitを使った場合、それ以後のルールの実行は行なわれないが、 ENDルールだけは(記述されていれば)実行される。もし、このような場合に ENDルールを実行したくないというのであれば、 exit文の前である変 数に0以外の値をセットし、その変数を ENDルールの中でチェックすれば良い。

例えばエラーが発生し、それを扱う手段がない場合にはそれをしらせる。慣例として、 プログラムは0以外のステータスを返すことによって、エラーの発生を報告する。 awk プログラムはそれをexit文に 0(成功)以外の引数を与えることに よって行なうことができる。例を挙げよう。

BEGIN {
       if (("date" | getline date_now) < 0) {
         print "Can't get system date" > "/dev/stderr"
         exit 4
       }
}

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