if
やwhile
などの制御文(control statement)は、
awk
プログラムの実行の流れを制御する。
awk
の制御文のほとんどはCで使われているものと
同じ形式である。
すべての制御文は単純な式と区別するために、
if
やwhile
などのような特別なキーワードで始まる。
多くの制御文は他の文を構成要素に持っている。例えばif
文は
実行されたりされなかったりするbody
と呼ばれるような
別の文を持っている。
もしbody
に二つ以上の文を含めたいのであれば、カーリーブレースを
使うことにより、セミコロンや改行で区切られた文の集まりを一つの
複合文としてまとめることができる。
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
文を実行する。
else
がthen-bodyと同じ行にあり、then-bodyが複合文でない
(カーリーブレースで囲まれていない)とき、セミコロンで then-body と
else
を区切らなければならない。この規則により、先の例は次のように書け
る。
if (x % 2 == 0) print "x is even"; else print "x is odd"
ここで`;'を付け忘れてしまった場合、awk
は構文解析を続けられなくな
り、シンタックスエラーとなる。
我々は実際にはこの例のような書き方はしない。なぜなら人が読むときに、行の最初に
なければelse
を読み落す可能性があるからである。
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にセットし、その後に
while
がi
が3以下であるかどうかを検査する。 i
がある数字で
あったとき、i
番目のフィールドが出力される。i++
によってi
の値が増やされて、ループが繰り返される。ループはi
が4になったときに終
了する。
例を見るとわかる通り、改行は条件と本体の間になくても良い。しかし、本体が複合 文か非常に単純なものでない限り、改行を入れることによってプログラムの見通しが よくなる。複合文の初めにある左カーリーブレースのあとの改行はなくても構わない が、プログラムが読み辛いものになるだろう。
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
文を使うことというのはまれであるからで
ある。
for
Statement
for
文はループの繰り返しを数えるのに便利である。一般的なfor
文は
次のような形をしている。
for (initialization; condition; increment) body
initialization, condition, increment
といった部分はawk
の式を置くことができ、bodyは
awk
の文を表わしている。
for
文はinitializationの実行で始まり、続いてcondition
が真である間繰り返してbody と incrementが実行される。典型的
な例では、initializationで変数に0や1をセットするということが行なわ
れ、incrementでその変数に1加えられる。そしてcondition では
それが繰り返しの回数を決める数と比較される。
for
文を使った例を挙げよう。
awk '{ for (i = 1; i <= 3; i++) print $i }' inventory-shipped
この例では、入力レコードごとに最初の三つのフィールドを一行にひとつのフィール ドで出力する。
for
文の中ではbodyは文であるが、initializationと
condition 、 incrementは式である。 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
ループの例を挙げる。
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を参照.)。
次の例は先の例と等価な別のプログラムである。これは、for
や while
の条件が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.)。
continue
Statement
continue
文は break
と似ていて、
for
、 while
、do
-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.)。
next
Statement
next
文は強制的にawk
に対して、即座にカレントレコードの処理を中
断させ、次のレコードの処理に移させる。つまり、その先のルールはカレントレコー
ドに対して適用されないということである。同様に、その時点で適用されているルー
ルのアクション部の残りもまた実行されない。
これと対照的な効果をもたらすのがgetline
関数
(セクション getline
を使った入力を参照)である。
getline
はawk
に次のレコードを即座に読み込みさせるが、その後の
制御は変更しない。したがって、その時点で実行しているアクションの残りの部分は、
新しい入力レコードに対して行なわれる。
最も高いレベルでは、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
ルールにあるコードが実行される。
セクション 特殊パターンBEGIN
とEND
を参照.
警告: 幾つかのawk
処理系には、ユーザー定義関数の中で
next
文を使うと実行時エラーを生成するものがある
(セクション ユーザー定義関数を参照)。
gawk
にはこのような問題はない。
nextfile
Statement
gawk
はnext
文に似た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
のバージョンではサポートされなくなるだろう。
exit
Statement
exit
文はawk
に対して即座に現在処理しているルールの実行をやめさ
せ、入力の処理もやめさせる。残った入力は無視される。
exit [return code]
exit
文がBEGIN
ルールで実行された場合、プログラムは停止し、すべ
ての処理も即座に停止して、入力レコードは読まれない。しかし、END
ルール
がある場合にはそれが実行される。
(セクション 特殊パターンBEGIN
とEND
を参照).
exit
が END
ルールで使われた場合には
プログラムは即座に停止する。
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") }