講座「文系のためのawkプログラミング入門」
前回まではアクション内での、スクリプトの実行順序の制御について見てきました。今回は、アクションの外側、BEGIN部に始まってEND部に終わるawkのスクリプト全体の実行順序について補足しておきます。
第1回で述べたように、awkのスクリプトは
BEGIN{アクション}
パターン1{アクション}
パターン2{アクション}
パターン3{アクション}
------------
END{アクション}
という構造をしています。入力ファイルを読み込む前に、BEGIN部が実行され、次に入力ファイルを一行読み込む度に、パターン1{アクション}、パターン2{アクション}、パターン3{アクション}以下が実行されます。END部の直前まで実行すると、あらためて入力ファイルから次の一行を読み込んで、同様の実行を繰り返します。全部の行を読み込んで、処理が終わると、最後にEND部を実行して終了します。この基本的なスクリプトの実行順序を変更するのが、exit や next 命令である。
次のスクリプトは、ファイルの中で、"word" という綴りの出現する行数をカウントして、50回を超えたら、ファイル名を出力して、処理を終了します。
/word/{ times++ # 入力行に word という綴りが出現するたびに times はインクリメント if(times > 50) { print FILENAME # FILENAME は組込み変数で入力ファイルをフルパスで表す。 exit } }
next 文は exit文より少し複雑です。next 文は、アクションの中に置かれますが、残りの処理を中断し、さらに、残りのパターンの照合も中断して、ただちに次の入力行の処理を新しく始めます。while 文や for 文の continue 文と同様の働きをします。
次のスクリプトは、パターン/pattern1/にマッチする行にはついては action1 を、パターン/pattern2/にマッチする行には action2 を実行、パターン/patternN/まで同様にパターンの照合を上から順序良く行います。いずれかのパターンにマッチするとそこでアクションが行われるが、それ以降のパターンの照合は行わず、次の入力行を読んで、最初の/pattern1/の照合から処理を繰り返します。
/pattern1/{ action1 next #以下のパターンマッチングを省略して、次の入力行の処理へ } /pattern2/{ action2 next #以下のパターンマッチングを省略して、次の入力行の処理へ } /pattern3/{ action3 next #以下のパターンマッチングを省略して、次の入力行の処理へ } ----- /patternN/{ actionN }
このスクリプトで、next がないと たとえば/pattern1/ にマッチして、action1 が実行された後も、その同じ入力行について、/pattern2/ /pattern3/ --/patternN/ までの照合が行われ、もしマッチするならば、それに対応する action が実行されます。
たとえば、次のような入力ファイル data15.txt を用意して、動作を確かめてみましょう。
東京、大阪、名古屋 東京、大阪、名古屋 東京、大阪、名古屋 東京、大阪、名古屋
このファイルを次のようなスクリプトで処理してみます。
# script 15-1 /東京/{ print "東京出身だね" } /大阪/{ print "大阪出身やね" } /名古屋/{ print "名古屋出身だがや" }
Input欄 File Output 欄 Screen で実行してみて下さい。各入力行に対して、3回のパターンマッチングが行われ、それぞれで print 文からの出力がありますから、合計で12行の出力があります。これに、next 文を下のように加えると出力はどうなるか分かりますか。実行する前にしばし、予想してみましょう。
# script 15-2 /東京/{ print "東京出身だね" next # この行を追加 } /大阪/{ print "大阪出身やね" } /名古屋/{ print "名古屋出身だがや" }
結果は、東京人が4回出てくるだけです。大きな変化ですね。これは next 文のため /大阪/ や /名古屋/ のパターンマッチが行われないからです。行を読み、最初の/東京/のパターンマッチングに合格して、print 文を実行しますが、その後の、next 文で、残りのパターンマッチングを省略して、すぐ次の行の読み込みを行います。この結果、第一のパターンマッチの結果しか出力されないのです。
awkの動作に影響を与える命令としては、getline があります。この命令と、next は、全く異なった種類の命令です。しかし、現在の入力行の処理が終わると言う点では共通点もあります。getline の場合も既に読み込んでいた入力行の処理は中断します。これは next 文 と同じです。しかし、getline の場合はその場で、次の入力行を読み込みます( getline は、次の line 行を読み込めという意味ですね)。その結果、アクションの残りが新しく読み込んだ行に対して実行されます。従って、そのアクションの処理が終わると、次のパターンマッチングも行います。
これに対して next 命令は、それ自体としては、新たな行の読み込みを行いません。単に現在のアクションを中断し、従って、残りのパターンマッチングもキャンセルし、次の入力行の処理を最初からやるようにawkに伝えるだけです。 15-2 のスクリプトの next 命令を getline に置き換えてみると、その動作がの違いがはっきりします。スクリプトを下のように書き換えて実行してみて下さい。
# script 15-3 /東京/{ print "東京出身だね" if(getline == 0) #ファイルの終わりの場合の処理 exit } /大阪/{ print "大阪出身やね" } /名古屋/{ print "名古屋出身だがや" }
結果は、以下の如し。
東京出身だね(1) 大阪出身やね(2) 名古屋出身だがや(2) 東京出身だね(3) 大阪出身やね(4) 名古屋出身だがや(4)
東京人が2回、大阪人、名古屋人がそれぞれ2回ずつ登場します。このスクリプトでは、ファイルの1行目については/東京/のパターンマッチに合格して、最初のアクションが実行されます。しかし、そのアクションの中で getline を実行し、ファイルの2行目を読みます。この2行目に関しては /東京/ のパターンマッチングは行われません。次の /大阪/ のパターンマッチングと/名古屋/のパターンマッチングが行われ、それぞれprint文が実行されます。そのあと3行目が新たに読み込まれますが、ファイルの1行目の時と同様に、/東京/のパターンマッチングに合格、print文が実行されますが、そのあとの getline で4行目が読み込まれます。これに対しては、/大阪/と/名古屋/のパターンマッチングが行われます。5行目はないのでこれで終了します。この動きはけっこう複雑です。getline の位置を、/大阪/ や /名古屋/ のアクションの中に移したりして、いろいろ実験してみて下さい。
exit、next 文について見てきました。またgetline の復習をやりました。こういうスクリプトの実行順序に影響を与える命令は単純な算術関数や文字列関数の使い方よりも、ややむずかしいかもしれません。実際のスクリプトを使う中で慣れてください。
今回で、スクリプトの実行順序を制御する方法についての学習は終わりです。次回は、多くのデータを効率よく取り扱うための、awkの配列について学びます。