awk
awk の基本的な機能は、テキストファイルからあるパターンを含む行、もし
くは別の単位のテキストを検索するということである。パターンの一つに行がマッ
チしたとき、 awk はその行に対して特別な動作をする。
awk は入力が入力ファイルの終端に達するまで、この動作をすべての入力
行に対して行う。
awk を実行したときにawk がどのように動作するかをawk プ
ログラムで指定できる。プログラムはルールの集まりからなる(関数定義も含ま
れるが、高度な機能なので今のところは無視する
セクション ユーザー定義関数を参照.)。
個々のルールはある特定のパターンを探し、見つかったパターンで定義されたアクショ ンを実行する。
文法的には、ルールはパターンとそれに続くアクションから構成される。アクショ
ンはカーリーブレースによってアクションと区切られる。ルールは一般的には改行
によって区切られる。その結果、 awk プログラムは以下のような形式とな
る。
pattern { action }
pattern { action }
...
次のプログラムは`BBS-list'という入力ファイルから`foo'という キャラクタの並びを探し出す。 (キャラクタの並びは通常、文字列と呼ばれる)
awk '/foo/ { print $0 }' BBS-list
行の中に`foo'が見つかると、その行が出力される。これは、 `print $0'がカレント行の出力を意味するからである。(代わりに `print'と書いても同じ結果が得られる)
`foo'を囲むスラッシュは検索するパターンを表している。こういったパター
ンは正規表現と呼ばれる。正規表現については後の方で詳しく述べられる
(セクション パターンとしての正規表現を参照)。
awkプログラムを引用符で囲んでいるのは、シェルのスペシャル
キャラクタがあっても、それをシェルに解釈させないようにするためで
ある。
プログラムの出力は次のようになる
fooey 555-1234 2400/1200/300 B foot 555-6699 1200/300 B macfoo 555-6480 1200/300 A sabafoo 555-2127 1200/300 C
awkのルールでは、パターンかアクションのどちらかを省略することができ
るが、両方とも省略することはできない。パターンが省略されると、(そのパター
ンに対応する)アクションはすべての入力行に対して実行される。アクションが省
略されていると、デフォルトのアクションとして(パターンにマッチした)その行
を出力する。
したがって、先の例ではアクション(print文とそれを囲むカーリーブレー
ス)を省略でき、同じ結果、つまり`foo'とマッチする行を出力する。を得る
事ができる。しかし、カーリーブレースは省略せずにprint を省略した場合
には何も行わず、行も出力されない。
awkは、一度のファイル入力で一行だけ入力する。入力された個々の行に対し
て、awkプログラムで記述されている各ルールのパターンと照合する。パター
ンとマッチすればアクションが実行される。マッチするパターンがなければ何も実行
されない。
ある入力行に対して、すべてのルールを(という事はあまりないだろうが)適用し
た後でawkは次の入力行の読み込みを行う(しかし例外はある。
セクション next文を参照)。この動作はファイルの
終端に達するまで繰り返される。
例えば次のawkプログラム、
/12/ { print $0 }
/21/ { print $0 }
この例は二つのルールから構成される。最初のルールはパターンとして 文字列`12'を、アクションとして `print $0'を持つ。二番目のルールは、 パターンとして文字列 `21'を、アクションとして一番目のルールと同じ `print $0'を持つ。各ルールはペアとなっているブレースによって囲まれてい る。
この awk プログラムは、文字列12か文字列21を含むすべての行を出力する。
もし、行の中に両方ともあれば、その行はそれぞれのルール毎に、結果として二回
出力される。
もし、このプログラムを先程の `BBS-list' と`inventory-shipped' の二 つのサンプルデータファイルを使って実行させるには次のようにする。
awk '/12/ { print $0 }
/21/ { print $0 }' BBS-list inventory-shipped
出力として得られるのは以下の内容である。
aardvark 555-5553 1200/300 B alpo-net 555-3412 2400/1200/300 A barfly 555-7685 1200/300 A bites 555-1675 2400/1200/300 A core 555-2912 1200/300 C fooey 555-1234 2400/1200/300 B foot 555-6699 1200/300 B macfoo 555-6480 1200/300 A sdace 555-3430 2400/1200/300 A sabafoo 555-2127 1200/300 C sabafoo 555-2127 1200/300 C Jan 21 36 64 620 Apr 21 70 74 514
ノート: `BBS-list' 中の`sabafoo'で始まる行が二回出力されているのは 各ルールで一回ずつ出力されたからである。
この節では、あなたが awkプログラムを書こうとするときになんらかのヒン
トになるようなことが記述されている。この例では、 awkを他のユーティリ
ティーの出力を要約したり、選択したり、ならべ直すのに使えることを示している。
ここまでで説明されていない機能が使われているが、全体のディティールを理解でき
なくとも、気にすることはない。
ls -l | awk '$5 == "Nov" { sum += $4 }
END { print sum }'
このコマンドはカレントディレクトリにある11月(年は何年でもよい)に最後の修 正がなされたファイルの大きさの合計バイト数を出力する。 (あなたがCシェルを使っ ていた場合、このサンプルを実行するには最初の行の最後にセミコロンとバックス ラッシュを付け加える必要がある。 POSIX に従ったシェル、例えばBシェル であるとか、BASHを使っている場合にはサンプルをそのまま打ってみてかまわない)
例の`ls -l' の部分は、ディレクトリ中のファイルをそのサイズと 修正日付とともにリストアウトするためのコマンドであり、その出力は 以下のような形である。
-rw-r--r-- 1 close 1933 Nov 7 13:05 Makefile -rw-r--r-- 1 close 10809 Nov 7 13:03 gawk.h -rw-r--r-- 1 close 983 Apr 13 12:14 gawk.tab.h -rw-r--r-- 1 close 31869 Jun 15 12:20 gawk.y -rw-r--r-- 1 close 22414 Nov 7 13:03 gawk1.c -rw-r--r-- 1 close 37455 Nov 7 13:03 gawk2.c -rw-r--r-- 1 close 27511 Dec 9 13:07 gawk3.c -rw-r--r-- 1 close 7989 Nov 7 13:03 gawk4.c
最初のフィールドは読み書きの許可フラグ、二番目のフィールドはそのファイルへ リンクしているリンクの数、三番目のフィールドはそのファイルのオーナーのID、 四番目がそのファイルの大きさをバイトで表したもの、5,6,7番目のフィールドは 最後にそのファイルが修正された月、日、時間である。 最後の8番目のフィールドはファイルの名前である。
このawkプログラムの$5 == "Nov"という式は、 `ls -l'の
出力の五番目のフィールドが`Nov'という文字列とマッチするかどうかをテス
トしている。 `Nov'という文字列を五番目のフィールドに持つ行が読み込まれ
るたびに `{ sum += $4 }'というアクションが実行される。このアクション
では、四番目のフィールド(ファイルサイズ)をsum という変数に足し込ん
でいる。結果として、sumはパターンにマッチした行のファイルの大きさの
合計を表す(awkの変数は、自動的に 0で初期化されている)。
ls の出力から渡される最後の行が処理された後で、ENDルールが実
行され、 sumの値が出力される。この例では、 sumの値は80600にな
るだろう。
こういった類の高度なawkの使い方は、後の方のセクションで述べられている
(セクション アクションの概観を参照)。
けれども、その様な使い方を覚える前にどのように入力が解釈され、どのように
出力が表示されるかを知るべきだろう。
フィールドを操作し、print文を使うことによって、有用なそして華やかな
見栄えのするレポートを作成することができる。
ここではawkプログラムの実行のしかたを説明する。
プログラムが短ければ、次のようにawkを実行するコマンドに含めて
しまうのが簡単である。
awk 'program' input-file1 input-file2 ...
前にも述べたように、programはパターンとアクションの並びから 構成されている。
プログラムが長くなったときには、(そのプログラムを)ファイルにして 次のようにコマンド指定して実行させるのが便利だろう。
awk -f program-file input-file1 input-file2 ...
awkプログラム
一度awkに慣れれば、必要とするときに単純なプログラムをタイプする
こともしばしばあるだろう。そういったとき、次のようにawkコマンドの
最初の引数としてプログラムを書くこともできる。
awk 'program' input-file1 input-file2 ...
以前に述べたように、programはパターンとアクションの並びから 構成されている。
この様なコマンド指定はシェルに対して、awkを起動してその入力ファイルに
program を適用する様に指定している。このプログラムを囲む引用符は、
awkのキャラクターを(シェルの)スペシャルキャラクタとして不用意に解釈
されるのを防ぐためにある。また、シェルが引用符に囲まれたprogram全体を
一つの引数として扱うようにもして、プログラムが一行以上の長さを取れるようにし
ている。
この書式は短かったり、あるいは少し大きいという程度のawkプログラムを
シェルスクリプトから実行させるのに便利である。なぜなら、そうすることによって
awk プログラムを独立したファイルとしておかなくてすむからである。こう
いったシェルスクリプトは、プログラムファイルを間違った場所に置くようなことが
ありえないので便利である。
awkを実行
入力ファイルなしに、awk を実行する事もできる。
コマンドラインで次のように打ち込んでみてほしい。
awk 'program'
それからawkはprogramをstandard inputに対して適用する。
つまり、あなたがターミナルから何かタイプするものすべてを入力として扱う。
この動作は、あなたがControl-d、つまりファイルの終端を示すコードを
入力するまで続けられる(訳注: MS-DOSではControl-zを使う)。
例えば、次のコマンドを実行すると、
awk '/th/'
その後で入力したテキストはawkプログラムに対するデータとして読み込まれ
る。たとえば、次のようなデータを続けて入力したとすると、
Kathy Ben Tom Beth Seth Karen Thomas Control-d
awkは次のような出力をするだろう。
Kathy Beth Seth
`th'がマッチするパターンであるが、`Thomas'はマッチするパターンとし
て認識されない。それは、awkは大小文字を区別してパターンの
マッチングを厳密に行うからである(しかし、組み込み変数IGNORECASEを
使って動作を変更することができる)。
セクション 大小文字を意識した照合を参照.
ときとして、あなたの作るawkプログラムは非常に長いものになるかもしれない。
このようなときには分割したファイルにプログラムを入れるのがよいだろう。
そのようにしたプログラムのファイルを使うためには次のようにすればよい。
awk -f source-file input-file1 input-file2 ...
`-f'はawkユーティリティにawkプログラムをファイル
source-fileから得る様にしろ、という指示を行う。 source-fileとし
て、どのようなファイル名も使うことができる。たとえば、
/th/
これをファイル`th-prog'に記述して、次のようなコマンドをタイプする。
awk -f th-prog
これは次のようにするのと同じである
awk '/th/'
これは以前に説明されている。
(セクション 入力ファイルなしでのawkを実行を参照)
ほとんどのファイル名はシェルのスペシャルキャラクタを含む様なことはないから、
通常はファイル名を引用符で囲む必要はない。
また、`th-prog'中のawkプログラムも引用符で囲まれていない。
引用符はコマンドラインにプログラムを置くようなときにだけ必要なのだ。
awkプログラムのファイルであることを明確にわかるように
ファイルの拡張子として`.awk'をファイル名に付け加えるとよいだろう。
この拡張子はawk プログラムの実行に関しては何の影響もないが、
"housekeeping" を簡単にするのだ。
awk プログラム
一度awkを学べば、シェルの`#!' 構文を使って、独立した
awk スクリプトを書いてみたくなるだろう。多くのUNIXシステム
(1)
でこの機構を使う事ができる(もちろんいつの日にか完成するだろうGNUも含まれる)。
例えば、次のような構造のプログラムファイル(`hello'というファイル名で 呼ぼう)を作成する事ができる(BEGINという機能はまだ説明されていない)。
#! /bin/awk -f
# a sample awk program
BEGIN { print "hello, world" }
ファイルを作ったらそれを実行してみよう(もちろんchmodコマンドを実行
してから、だが)。実行するには単にこうタイプすれば良い。
hello
シェルやシステムによってはawkの実行のしかたに多少の違いがあるだろう。
(2)
awk -f hello
独立したawkスクリプトは、ユーザーにそれがawkで記述されたか
どうかを気にさせないようなプログラムを記述するのに便利である。
もしあなたの使っているシステムが`#!'機構のサポートをしていないものであっ ても、普段使用しているシェルスクリプトを使って同様の効果を得る事ができる。 そのためには次のようにすればよい。
: The colon makes sure this script is executed by the Bourne shell. awk 'program' "$@"
このテクニックを使うとき、引用符でprogramを括ることは非常に重要である。 それは、引用符で括ることによってシェルが解釈してしまうことを防いでいるからで ある。引用符を忘れると、シェルに詳しい人だけが実行結果を予測できるような事態 になるだろう。
`"$@"'は、シェルにコマンドライン引数をそのまま(シェルが解釈する事な
しに)awk プログラムに引き渡す。コロンで始まっている最初の行は、この
シェルスクリプトを(Cシェルを使っている人が起動しても)、ちゃんとスクリプト
が動作するようにするためである。
コメントとはプログラム中で、人が(プログラムを)読みやすくするために含 められるテキストであり、プログラムにとって必要不可欠というものではない。コメ ントはプログラムが書けるところならどこでも置けるが、何の働きもしない。身近な 全てのプログラミング言語はその様な目的のためにコメントを使える様になっている が、それは典型的なプログラムというものは何らかの助けなしに理解するのが非常に 難しいからである。
awk言語のコメントは`#'で始まり、その行の終わりまで続く。
awkは`#'に続く部分を無視する。次のプログラムを`th-prog'に記
述してみよう。
# このプログラムは `th'が含まれるレコードを探し出す。 # これはコメント行の付けかたである /th/
awkプログラムをキーボードから直接入力しているような場合でもコメント行
を入力することは可能であるけれども、これはあまり有効なことではないだろう。な
ぜならコメントを使う目的が、後日に他の人がそのプログラムを読んだときに理解す
るのを助けるためだからだ。
多くの場合、awk programの各行は次の例にみられるように独立した文、ある
いは独立したルールである。
awk '/12/ { print $0 }
/21/ { print $0 }' BBS-list inventory-shipped
それでも時として、一つの文が複数行にわたるようなことがあるかもしれないが、そ の様なときは、次に挙げるものの後に改行を入れることで、あるステートメントを複 数行に分割して記述することができる。
, { ? : || && do else
その他の場所にある改行は文の終端として認識される。 (`?'と`:' の後
の行の分割はgawk特有の拡張である `?' と `:'三つのオペランド
を取る条件式であり、詳しい説明は
セクション 条件式を参照.)
一つの文を二つの行に改行で分けたいときに、行末にバックスラッシュ`\'を置 くことによって行を継続することができる。このバックスラッシュによる行の継続は どこにでも置く事ができる。たとえ、文字列や正規表現の途中であっても可能である。 例えば
awk '/This program is too long, so continue it\
on the next line/ { print $1 }'
このマニュアルのサンプルプログラムでは、バックスラッシュによる行の継続を
その行の長さが限界を超えてしまうような厳しい理由がない限りは
使用しない。それによってプログラムが見やすくなるからである。
つまり、一つの文を短く保つことによって、見やすさを確保しているのである。
バックスラッシュによる継続はあなたの作成する awkプログラ
ムがコマンドライン上にタイプされたものではなく、独立したファイルに収まってい
るのであれば非常に使い易いものだろう。作成するawkプログラムに最大限の
移植性を求めるのならば、正規表現や文字列の途中での行の分割を行わないこ
とが最良の手段だろう。
警告:バックスラッシュによる行の継続はCシェルではこのマニュアルに
書かれている通りには動作しない。 バックスラッシュによる行継続はファイルに記
述されたawkプログラムや Bシェル、BASH等の POSIX に従ったシェルでの
one-shot programsでは働くが、 Berkeley Unixで使われているCシェルではそうはな
らず、バックスラッシュを改行の前に二つ続けて書かなければならない。
一つのルールの中にあるawkの文が短いとき、複数の行を一つの行でまとめて
書きたいと思うかもしれない。そのようなときは文をセミコロン`;'で区切れば
よい。このことはルールそれ自体にも使うことができるので、先のプログラムは次の
ようにも書ける。
/12/ { print $0 } ; /21/ { print $0 }
ノート: 同一行に置かれたルールはセミコロンによって分割せねばならない。
ということを要求している。これはawk言語における最近の変更であり、これは
また、アクション中の文についても同じ様に適用される。
あなたはawkが非常に便利であるのに戸惑いを感じるかもしれない。他のユー
ティリティプログラムや、より高度なパターン、フィールドセパレータ、算術式、さ
らに他の選択基準を使ってより複雑な出力を作り出すことができる。 awk 言
語はlsなどの他のユーティリティプログラムの出力の様な大きなデータの羅
列からレポートを作成するのに非常に便利である
(セクション より複雑な例を参照.)。
awkでプログラムを書くと通常は他の言語で記述したときよりも小さなサイ
ズで書けるだろう。このことは、awkプログラムを作ったり、使ったりする
のが簡単だという事だ。awkプログラムはしばしば、さっとターミナルから
タイプして一回だけ使って使い捨てにできる。 awkプログラムはインタプリ
タで実行されるので通常のエディット−コンパイル−テスト−デバッグという開発
のサイクルを経ずにすむのである。
8bitマイクロコンピュータ用のアセンブラ(セクション 用語集を参照, formore
information) や特殊な目的のPrologコンピュータ用のマイクロコードアセンブラの
ような複雑なawkプログラムも書かれたこともあるが、awkの能力か
らいえばそのいった複雑な仕事をさせるのは少々酷使にすぎるだろう。
2、300行以上のawkスクリプトを書くようになったとき、違ったプログラミ
ング言語の使用を考えていることに気がつくだろう。 Emacs Lisp は、文字列やパ
ターンマッチングを扱う高度な能力を必要とするときにはよい選択といえる。シェ
ルもまた文字列やパターンマッチングを扱う能力があり、それに加えて強力で便利
なシステムユーティリティを使う事ができる。より伝統的な言語、CやC++、Lispと
いったものは、システムプログラミングや大きなプログラムの複雑さを管理するの
に便利であるだろう。これらの言語で記述したプログラムは、同じ作業をする
awk プログラムよりも多くの行数を必要とするかも知れないが、より有効に
実行したり、簡単にメンテナンスできる。