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

アクション中の制御文

ifwhileなどの制御文(control statement)は、 awkプログラムの実行の流れを制御する。 awkの制御文のほとんどはCで使われているものと 同じ形式である。

すべての制御文は単純な式と区別するために、 ifwhileなどのような特別なキーワードで始まる。

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

The if-else Statement

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

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

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

例を挙げよう。

if (x % 2 == 0)
    print "x is even"
else
    print "x is odd"

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

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

if (x % 2 == 0) print "x is even"; else
        print "x is odd"

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

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

The while Statement

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

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

while (condition)
  body

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

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

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

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

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

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

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

The do-while Statement

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文を使うことというのはまれであるからで ある。

The for Statement

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

for (initialization; condition; increment)
  body

initialization, condition, increment といった部分はawkの式を置くことができ、bodyawkの文を表わしている。

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

for文を使った例を挙げよう。

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

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

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が省略されたならばそれはtrueが常に真 であると解釈され、無限ループ (終了せずに繰り返しを続けるループ)とな る。

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

initialization
while (condition) {
  body
  increment
}

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

次にfor文の別なバージョンの例を挙げよう。これは配列の添え字をすべて 繰り返す。

for (i in array)
    array[i] に対するなんらかの操作

この形式のforループに関する詳しい説明は セクション Scanning All Elements of an Arrayを参照。

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

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

The break Statement

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

awk '# find smallest divisor of num
     { num = $1
       for (div = 2; div*div <= num; div++)
         if (num % div == 0)
           break
       if (num % div == 0)
         printf "Smallest divisor of %d is %d\n", num, div
       else
         printf "%d is prime\n", num
     }'

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

次の例は先の例と等価な別のプログラムである。これは、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
         }
       }
}'

先に述べたように、break文をループ本体の外側で使うことは意味がない。 しかしながら、これはドキュメント化されてはいないのだが、awkの伝統 的な実装においては、ループの外側でbreakが使われた場合にはそこに next文が置かれていたのように動作する (セクション The next Statementを参照)。 最近のUNIX上のawkでは、このような使い方は許されていない。 gawk`--traditional'がコマンドラインオプションで指定されたと きのみ(セクション コマンドラインオプションを参照)、この動作をする。指定がなか った場合には、これはPOSIX標準ではbreakはループ本体内でのみ使うこと が規定されているので、エラー扱いとなる(d.c.)。

The continue Statement

continue 文は breakと似ていて、 forwhiledo-whileの中でだけ使われる。 continueはループ本体の残りの部分をスキップし、ループの次の サイクルを直ぐに始めさせる。これはbreakの動作、 ループの外側にジャンプするというのとはまったく対照的である。

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文であるか のように扱う (セクション The next Statementを参照)。最近 のUNIXのawkはこの使い方をサポートしている。gawk`--traditional'オプションがコマンドラインで指定された場合にこれをサ ポートす る。それ以外の場合はエラーとする。なぜなら POSIX 標準では continueはループ本体の内側で使わなければならないとされているから である(d.c.)。

The next Statement

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

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

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

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

NF != 4 {
  err = sprintf("%s:%d: skipped: NF != 4\n", FILENAME, FNR)
  print err > "/dev/stderr"
  next
}

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

POSIXの標準によると、next文がBEGINルールやENDルール にあるときの動作は未定義である。gawkはこれをシンタックスエラーで あるとみなす。POSIXでは許されていても、一部のawk処理系では nextが関数本体の内側に置かれることを許していなかった (セクション ユーザー定義関数を参照)。 他のnext文の使い方と同じように、関数本体の内側にあるnext文 は、次のレコードを読み込み、プログラムの最初のルールから処理を開始する。

next文の実行によって入力が終端に達したら、 ENDルールにあるコードが実行される。 セクション 特殊パターンBEGINENDを参照.

警告: 幾つかのawk処理系には、ユーザー定義関数の中で next文を使うと実行時エラーを生成するものがある (セクション ユーザー定義関数を参照)。 gawkにはこのような問題はない。

The nextfile Statement

gawknext文に似たnextfile文を提供している。これは、 nextが行っているようなカレントレコードの処理を放棄するものではな く、gawkに対してカレントデータファイルの処理をやめさせるものであ る。

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

nextfile文はgawkでの拡張機能である。この機能は(少なくとも現 在のところ)他のawk処理系では使用できない。 nextfileをシミュレートするのに使えるユーザー定義関数は セクション Implementing nextfile as a Functionを参照,

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

警告:3.0以前のバージョンのgawkでは、 nextfile文には二つの単語(`next file')を使っていた。 これは3.0で一つの単語に変更された。それは、 `file'の取り扱いが一貫性のないものであったからだ。 `file'nextの後に現れた場合、それはキーワードとなる しかし、それ以外の場合には通常の識別子になるのだ。 古い使用法もまだ受け付けられるが、gawkは 警告メッセージを出力するし、next fileは 将来のgawkのバージョンではサポートされなくなるだろう。

The exit Statement

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

exit [return code]

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

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

BEGIN ルールでも ENDルールでもないところで exit文を使った場合、それ以後のルールの実行は行なわれないが、 ENDルールだけは(記述されていれば)実行される。

もし、このような場合にENDルールを実行したくないというのであれば、 exit文の前である変数に0以外の値をセットし、 その変数を ENDルールの中でチェックすれば良い。 この例は セクション Assertionsを参照.。

exitに引数を与えた場合、その値はawkを実行している プロセスの終了ステータスの値として使われる。 引数がない場合には、exitは0(成功)を返す。 引数つきのext文を最初に実行し、その後で引数なしの exit文を実行すると、その前に使った引数が 終了ステータスとして使われる(d.c.)。

例えば、対処できないエラー状態になったときに、それを報告するとしよう。 一般的にはプログラムはこれを0以外の終了コードによって報告する。 awkプログラムはこれを、exit文を0以外の引数付きで 使うことによって実現できる。例を挙げよう。

BEGIN {
       if (("date" | getline date_now) <= 0) {
         print "Can't get system date" > "/dev/stderr"
         exit 1
       }
       print "current date is", date_now
       close("date")
}

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