全ての制御文はif
や while
といった特殊なキーワードで始まり、単純
な式とは区別される。 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
文を実行する。
else
がthen-bodyと同じ行にあり、then-bodyが複合文でない
(カーリーブレースで囲まれていない)とき、セミコロンで then-body と
else
を区切らなければならない。この規則により、先の例は次のように書け
る。
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にセットし、その後に
while
がi
が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が真である 間繰り返してbody と incrementが実行される。典型的な例では、 initializationで変数に0や1をセットするということが行なわれ、 incrementでその変数に1加えられる。そしてcondition ではそれが繰り 返しの回数を決める数と比較される。
for
文の例を挙げてみよう。
awk '{ for (i = 1; i <= 3; i++) print $i }'
この例では、入力レコードごとに最初の三つのフィールドを一行にひとつのフィール ドで出力する。
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が省略された場合には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
文を参照.)
次の例は先の例と等価な別のプログラムである。これは、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 } } }'
continue
文
continue
文は break
と似ていて、
for
、 while
、do
-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
を使った入力を参照)である。
getline
はawk
に次のレコードを即座に読み込みさせるが、その後の
制御は変更しない。したがって、その時点で実行しているアクションの残りの部分は、
新しい入力レコードに対して行なわれる。
最も高いレベルでは、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
ルールに
制御が移った場合それは実行される。
セクション スペシャルパターンBEGIN
とEND
を参照.
next file
文
next file
文はnext
文と同類である。しかし、カレント入力レコード
の処理を中断させるのではなく、next file
はawk
に対して現在処理
しているデータファイルの処理を中断させるのである。
next file
文の実行によって、FILENAME
の内容はコマンドラインの次
のファイルの名前にアップデートされ、FNR
は1にリセットされてプログラム
の最初のルールから処理が開始されるセクション 組込み変数を参照.。
next file
文の実行が入力の最後に到達させ、END
ルールに
制御が移った場合それは実行される。
セクション スペシャルパターンBEGIN
とEND
を参照.
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
ルール
がある場合にはそれが実行される。
(セクション スペシャルパターンBEGIN
とEND
を参照).
exit
が END
ルールで使われた場合には
プログラムは即座に停止する。
通常のルールの中で(つまり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 } }