1991年4月1日

例題で学ぶ 簡易言語「AWK」

原案
弘前大学 教育学部
教育実践研究指導センター
小山智史
データの差し替え、copalでの使用向けに書き換え
大阪教育大学 国語教育講座
野浪正隆

入門編

1. はじめに

 簡易言語「AWK」について説明します。AWKは、パソコン(MS-DOS)でもワークステーション(UNIX)でも利用できますが、ここでは、パソコンでの利用を前提に説明します。この資料はAWKのことを網羅しているわけではありませんが、代表的な使い方を身近な例題で紹介しています。実際に操作しながら読んでください。
 「習うより慣れろ」とは良く言われますが、ことコンピュータに関しては、良いお手本を真似ることから始めるのが一番良いように思います。そして、マニュアルの索引で必要なことを調べれば、おそらく8割程度はそのソフトを使いこなすことができるのではないでしょうか。
 AWK言語は、他の言語もそうであるように万能ではありません。プログラミング言語あるいはアプリケーションソフト(最近はプログラム可能なアプリケーションソフトも増えている)には、それぞれ得意とする分野があります。「やりたいこと」に適した道具を選択することが重要です。AWKによるプログラミングのトレーニングは、他の言語やアプリケーションソフトを使う時にも役立つことと思います。

2. 簡単なデータ処理

2.1 入力データ

 まず、アイドル?の「名前, 出身地, 身長, 体重」の4項目のデータが、データファイル"アイドル?.txt"に用意されています。メモ帳などで"アイドル?.txt"を開いておきましょう。(小さなウィンドウサイズでかまいません)

南野陽子	兵庫	162	43
相原勇	広島	156	40
堀ちえみ	大阪	156	45
若村麻由美	東京	163	45
早見優	静岡	164	47
森尾由美	埼玉	162	46
松本明子	香川	152	43
大沢逸美	北海道	168	58
小泉今日子	神奈川	155	42
三田寛子	京都	160	45
柏原芳恵	大阪	157	45
中森明菜	東京	160	47
松本伊代	東京	157	41
沢口靖子	大阪	159	46
小林聡美	京都	155	45
杉田かおる	東京	157	50
荻野目慶子	千葉	152	42
伊藤麻衣子	愛知	155	45
高樹沙耶	静岡	162	46
河合奈保子	大阪	159	48
今井美樹	宮崎	168	44
香坂みゆき	神奈川	157	44
松田聖子	福岡	159	40
斉藤慶子	熊本	163	45
岩崎良美	東京	158	45
手塚理美	東京	160	43
黒木瞳	福岡	163	46
浅野ゆう子	兵庫	167	50
東ちづる	広島	162	48
 このようなデータファイルはテキストエディタを使って簡単に作ることができます。

課題1:自分のデータを入力しよう

アイドル?.txtに自分の「名前 出身地 身長 体重」の4項目のデータを追加しよう。
各項目の間はTab(キーボードの左端上から三つめのキー)で区切ります。全角スペースで位置を揃えても、4つのフィールドに区切られません。

 ここで、各行を「レコード」と呼びます。南野陽子に関する情報は1番目のレコードです。

  名前、出身地、身長、体重
の各項目のことを「フィールド」と呼びます。身長の情報は3番目のフィールドに書いてあります。
 上のデータファイルは、レコードは行単位になっていて(すなわち各レコードの区切りは改行)、各フィールドはスペースやタブで区切られています。

2.2 プログラムの実行

 「体重が48kg以上のデータ」を表示するAWKプログラムは、次のとおりです。

$4>=48
ここで、"$4"は「4番目のフィールド」を表しています。このようなプログラムを、既に用意されているデータファイルに作用させて、希望するデータを表示するにはどうするかというと、まずは、プログラムファイルを作ります。 copalを使うときは、 これで、「体重が48kg以上のデータ」がディスプレイに表示されます。

例1:
$4>=48
例1の出力
大沢逸美	北海道	168	58
杉田かおる	東京	157	50
河合奈保子	大阪	159	48
浅野ゆう子	兵庫	167	50
東ちづる	広島	162	48

 さて、上の例では"$4>=48"がプログラムですが、これは実は

$4>=48{print($0)}
の省略形です。1番目のフィールド, 2番目のフィールド, ..., NF番目のフィールドは"$1", "$2", ..., "$NF"で表し、現在レコード(すべてのフィールド)は"$0"で表します。NFは現在レコードのフィールド数を値とする組み込み変数です(この例では毎回4になる)。

$NF>=48{print}

でも同様です。単に"print"とすると"print($0)"を意味します。同じ結果が得られるかどうか、実際にやってみて下さい。このプログラムは「最後のフィールドが48以上のレコードを表示しなさい」と読みます。

課題2:

体重が40kg以下のデータを表示するように、プログラムを書き換えましょう。

課題2: の解答

3. AWKのプログラム

3.1 プログラムの構成

 さて、前出の例で、"$4>=48"の部分を「パターン」、"print($0)"の部分を「アクション」と呼びます。一般に、プログラムは"パターン{アクション}"の並びで、

パターン1{アクション1}
パターン2{アクション2}
......
というようになっています。

 更に、場合によっては関数定義が加わりますが、これについては後で説明します。
 各行は「パターンにマッチしたレコードにアクションを実施しなさい」と読みます。
 プログラムは次のように実行されます。まず、データファイルの1番目のレコードに

パターン1{アクション1}
が適用され(すなわちパターン1がマッチすればアクション1が実行される)、次に
	パターン2{アクション2}
が適用され(すなわちパターン2がマッチすればアクション2が実行される)、というように、すべての"パターン{アクション}"が順に適用されます。次に、今度は2番目のレコードについて同様の処理が繰り返され、以下同様に最後のレコードまで繰り返されます。
 例を示します。

例2:
$4>=48{print($1,$4)}
$3>=160{print($1,$3)}
  1. 上の  を範囲指定し、 コピー(マウスの左ボタンをクリックしたまま右に移動して範囲を指定して、右クリックでポップアップメニューの中から「コピー」を選んで左クリック。あるいは、範囲指定した後、CTRL+C)したものを貼り付ける。
  2. アイドル?.txtを開いているメモ帳をアクティブにして(表示部分を左クリックするか、ALT+Tabを何回か押してメモ帳を青い線が囲むようにする)。CTRL+ACTRL+Cする。
  3. Copalのをクリックする。

例2の出力

南野陽子 162
若村麻由美 163
早見優 164
森尾由美 162
大沢逸美 58
大沢逸美 168
三田寛子 160
中森明菜 160
杉田かおる 50
高樹沙耶 162
河合奈保子 48
今井美樹 168
斉藤慶子 163
手塚理美 160
黒木瞳 163
浅野ゆう子 50
浅野ゆう子 167
東ちづる 48
東ちづる 162

 このプログラムは「体重が48kg以上のレコードは名前と体重を表示し、身長が160cm以上のレコードは名前と身長を表示しなさい」と読みます。1レコード毎にすべてのプログラムが実行される様子がわかると思います。
 パターンとアクションのうちいずれかを省略することができます。パターンを省略すると、すべてのレコードにマッチします。すなわち、

例3:
{print($0)}
というプログラムはデータファイルのすべてを表示します。また、既に例2のところで説明しましたが、
$4>=48
のようにアクションを省略すると
$4>=48{print($0)}
と同じことで、該当するレコードが表示されます。

3.2 パターン

 パターンには、真または偽の値をとる比較の式や、それらを組み合わせた式が用いられます。「体重が48kg以上でかつ身長が160cm以上の名前を表示しなさい」というプログラムは

例4:
($4>=48 && $3>=160){print($1)}
例4の出力
大沢逸美
浅野ゆう子
東ちづる
です。パターンを組み合わせる論理演算子としては、||(または)や &&(かつ)や !(否定)があります。このように、いくつかのパターンを論理的に結合して、欲しい情報を表示することが可能です。

課題3:

体重が40kg以下で身長が160cm以下のデータを表示するように、プログラムを書き換えましょう。

課題3: の解答

3.3 特殊なパターン

 特殊なパターンとして"BEGIN"と"END"があります。"BEGIN"はレコードの入力をはじめる前に1度だけマッチし、"END"はすべてのレコードの処理が終った後に1度だけマッチします。  例を示します。

例5:
BEGIN{print("名前	出身地	身長	体重")}
$4>=48{print($0)}
END{print("体重が48kg以上のアイドル?です.")}
例5の出力
名前	出身地	身長	体重
大沢逸美	北海道	168	58
杉田かおる	東京	157	50
河合奈保子	大阪	159	48
浅野ゆう子	兵庫	167	50
東ちづる	広島	162	48
体重が48kg以上のアイドル?です.

"BEGIN","END"をパターンとする文は有っても無くても構いません。また、複数あっても構いません。
 もうひとつの特殊なパターンとして、

パターン,パターン
という表現を使うことができます。これは、「はじめのパターンが現れてから次のパターンが現れるまで」という範囲指定になっています。
 例を示します。

例6:
NR==5,NR==10{print(NR,$1)}
例6の出力
5 早見優
6 森尾由美
7 松本明子
8 大沢逸美
9 小泉今日子
10 三田寛子

プログラム中のNRは、これまで読み込んだ通算レコード数、すなわち現在レコードの番号を値とする組み込み変数です。従って、5番目から10番目までのレコードが表示されています。

課題4:

7番目から12番目までのレコードを表示するように、プログラムを書き換えましょう。

課題4: の解答

3.4 文字パターンによるデータ検索

 文字パターンによるデータ検索の例を示します。まず、

/東京/{print($1)}
というプログラムは東京出身者を表示します。もっと正確に言えば、「レコードのどこかに東京という文字列が含まれていれば、そのレコードの1番目のフィールドを表示しなさい」ということになります。実行結果は次のとおりです。
例7:
/東京/{print($1)}
例7の出力
若村麻由美
中森明菜
松本伊代
杉田かおる
岩崎良美
手塚理美
この例では不都合は生じていませんが、「レコードのどこかに...」というのは不適当な指示で、本来ならば
$2=="東京"{print($1)}
とすべきところです。
課題5:

兵庫県出身者を表示するようなプログラムに書き換えましょう。

課題5: の解答

 次に、

例8:
/美/{print($1)}
とすると、「レコードのどこかに美という文字があれば......」ですが、
例8の出力
若村麻由美
森尾由美
大沢逸美
小林聡美
今井美樹
岩崎良美
手塚理美
例9:
$1~/美$/{print($1)}
とすると、「名前の最後が美という文字であれば......」になり、実行結果は次のとおりです。
例9の出力
若村麻由美
森尾由美
大沢逸美
小林聡美
岩崎良美
手塚理美
"/美$/"の'$'は「文字列の最後」を意味します。
 "$1~/ $/"は「1番目のフィールドが"/ /"で表されるようなパターンになっていれば ... 」と読みます。

課題6:

名前の最後が「子」であるレコードを表示するようなプログラムに書き換えましょう。

課題6: の解答

 はじめの例では、

/東京/{...}
となっていましたが、これは実は
$0~/東京/{...}
の省略形です。
 "/ /"で囲まれた部分は「正規表現」と呼ばれ、「欲しい情報」を的確にコンピュータに伝え、たくさんのデータの中からその情報を探し出すのに大きく寄与します。
 別の例を示します。

例10:
$1~/[野森田]/{print($1)}
例10の出力
南野陽子
森尾由美
三田寛子
中森明菜
杉田かおる
荻野目慶子
松田聖子
浅野ゆう子

 このプログラムは、「名前に'野'または'森'または'田'のいずれかが含まれている場合、その名前を表示しなさい」というものです。"[ ]"は、その中のどれか1文字を表しています。正規表現についてはこれ以上説明しませんが、付録Aに表記の一覧を示します。

課題7:

名前に'大'または'中'または'小'のいずれかが含まれているレコードを表示するようなプログラムに書き換えましょう。

課題7: の解答

3.5 アクション

 これまで示した例のアクションには print 文しか登場していませんが、一般には「文の並び」です。いくつかの文について、これ以降の例の中で取り上げていくことにします。
 特に、判断を行い処理の流れを変える制御文は重要ですので、注意して読んでください。

3.6 コメント

 文字列の中を除き、'#'が現れると、それ以降はコメントとして扱われます。

4. AWKにおけるデータ

4.1 データの型

 データの型は数値と文字列の2種類です。文字列データは"abc"のように"""で囲んで表します。数値データは、内部では浮動小数点データとして統一して扱われます。

4.2 定数と変数

 定数は、数値データまたは文字列データで、変数にはこれらの値を代入できます。変数は、データの型を指定することもなく、また宣言をすることもなく、突然使いはじめることができます。
 変数は、はじめは0または空文字列に初期設定されています。
 変数名は、アルファベットで始まる文字列です。大文字と小文字は区別して扱われます。大文字の変数名には、組み込み変数として使われているものもありますので、なるべく小文字を使うようにします。

4.3 配列

 配列は、大きさを指定することもなく、また配列の宣言をすることもなく、突然使いはじめることができます。そして、

x[1]=123
x[2]="abc"
のように、数値データと文字データを混在して使用することができます。
 更に、AWK言語の配列の大きな特徴は、
x["東京"]=123
x["山形県"]="abc"
のように、配列の添字を任意の文字列とすることができます。この機能を利用した例を次に示します。

例11:
{x[$2]++} ;
# 第2フィールド(アイドル?.txtでは、都道府県名)
# を配列の添え字として(都道府県名が書いてある箱を用意して、その中にある数に)、
# 1を足す。
END{for(i in x) print(i,x[i])};
# 最後の処理 xという配列のすべての要素
#(都道府県名が書いてすべての箱に対して、その中にある数 x[i])を
# printする。
例11の出力
静岡 2
大阪 4
埼玉 1
宮崎 1
広島 2
愛知 1
福岡 2
熊本 1
京都 2
香川 1
東京 6
神奈川 2
千葉 1
北海道 1
兵庫 2

どこの出身者が何人いるかが、すぐに求まります。「何という出身地が現れるかわからない」このような問題をうまく処理することができます。
 "x[$2]++"は、"x[$2]=x[$2]+1"と同じです。
 ENDのところの"for(i in x)"は、xという配列の添え字を、配列の中身が入っているところだけひとつずつ取り出し、iに代入してくれます。ただし、どういう順番で現れるかはわかりません。
 多次元配列として使う場合は、x[1,2]のように用います。
 他の言語では、配列はメモリ上で連続していますが、AWK言語の配列では、そうなっていないので注意が必要です。

課題8:

「羅生門」の構文解析結果を開き、どんなレコードであるかを確認して、CTRL+ACTRL+Cして、Copalのをクリックする。どうなったか?
CopalのScript画面の x[$2] をx[$3]に変えて、「羅生門」の構文解析結果をCTRL+ACTRL+Cして、Copalのをクリックする。どうなったか?
CopalのScript画面の x[$3] をx[$2 $3]に変えて、「羅生門」の構文解析結果をCTRL+ACTRL+Cして、Copalのをクリックする。どうなったか?

4.4 演算

 演算には、数値の演算と文字列の演算があります。論理演算は、1(真)と0(偽)を値とする数値の演算として扱われます。演算の一覧は付録Aにまとめてあります。

例12:数の計算
以下のプログラムを貼り付けたら、copal2のgoをクリックする。
BEGIN{
	y=10+2;
	#変数yに10+2の計算結果を代入する。
	print "y=",y;
	#変数yの値をprintする
}
y= 12
で、yには演算結果の12という値が代入されます。

課題9:円の計算

y=10+2をy=10*2に変えてcopal2のgoをクリックする。どうなったか?
y=10*2をy=10/2に変えてcopal2のgoをクリックする。どうなったか?
y=10/2をy=10-2に変えてcopal2のgoをクリックする。どうなったか?

y=10-2をy=11%2に変えてcopal2のgoをクリックする。どうなったか?
半径10cmの円の直径・円周・面積の計算結果を表示するプログラムを書きましょう。一カ所書き換えるだけで、半径100の場合も、120の場合も、256の場合も計算できるようにしましょう。

課題9: の解答

同様に

例13:文字列の連接
以下のプログラムを貼り付けたら、copal2のgoをクリックする。
BEGIN{
	y="abc" "de";
	print "y=",y;
}
y= abcde
とすると、yには演算結果である"abcde"という値が代入されます。y=10+2の場合は'+'と演算子を書きますが、後者ではスペースを置くだけです。連接は、文字列に関する唯一の演算です。
 変数はその型を宣言する必要がないばかりではなく、演算の途中で変換する必要が生じると、文脈に応じて自動的に変換されます。この例を次に示します。
例14:
以下のプログラムを貼り付けたら、copal2のgoをクリックする。
BEGIN{
	x=2;
	y=3;
	print((x y)+4);
}
はじめはxおよびyという変数には数が代入されます。しかし、次の(x y)は文字列の連接演算ですから、数字から文字列に自動的に変換され"23"という文字列になります。そして、その結果に4を加える演算ですから、数字に変換されて演算がおこなわれ、結局
27
が表示されます。
 C言語と同様にインクリメント演算子やデクリメント演算子が使えます。++は変数に1を加えるのですが、前置と後置では次のような差があります。
例15:
以下のプログラムを貼り付けたら、copal2のgoをクリックする。
BEGIN{
	x=1;
	y=x++;
	z=++x;
	print "X=",x;
	print "y=",y;
	print "z=",z;
}
とすると、yは1、zは3となります。これは、2行目はyにxを代入してからxの加算が行われ、3行目はxを加算してからzに代入が行われるからです。デクリメントについても同様です。

5. 関数

5.1 組み込み関数の利用

 関数は値を返します。ここでは個々の関数の説明は省き、例をいくつか示します。

sqrt(2)は1.414を返します。
int(3.14)は3を返します。
int(-3.14)は-3を返します。
length("abcde")は5を返します。
substr("abcde",3,2)は"cd"を返します。
jlength("弘前大学")は4を返します。
jsubstr("弘前大学",3,2)は"大学"を返します。
付録Aに組み込み関数の一覧を示してあります。

5.2 関数の定義と呼び出し

 関数を定義して利用する例を示します。

例16:

以下のプログラムを貼り付けたら、アイドル?.txtの内容をテキストコピーして、Copalのをクリックする。

{ x=max(x,$4) }
END{print(x)}
function max(m,n){ return(m>n?m:n) }
58
一番重い体重が表示されています。
 定義した関数"max(m,n)"はmかnの大きい方の値を返します。return文の中の式は
式1 ? 式2 : 式3
となっていますが、これは「式1が真ならば式2を評価してその値を返し、式1が偽ならば式3を評価してその値を返す」式です。return文でこれを関数の値として返しているわけです。この関数は、次のように書いても同じです。

例17:

以下のプログラムを貼り付けたら、アイドル?.txtの内容をテキストコピーして、Copalのをクリックする。

{ x=max(x,$4) }
END{print(x)}
function max(m,n){
	if(m>n)	return(m)
	else	return(n)
}
 ここで用いている「if文」は、制御文のひとつで、
if(式) 文 else 文
という書式です。式が真ならばはじめの文を実行し、偽ならばelseの後の文を実行します。制御構造については、付録Bを参照してください。
 関数定義で重要なことは、「引数は値で(コピーしたものが)渡される」ということです。したがって、
{
	a=1
	b=5
	x=max(a,b)
	print(a)
	...
}

function max(m,n){
	m=10
	...
}
のようにしたとしても関数の呼び出し側でaの値が変わることはなく、"print(a)"では'1'が表示されます。
 もうひとつ重要なことは、この関数定義の中で使われているmやnという名前の変数名は、実はこの関数の中だけで有効だということです。もしもこの関数定義以外のところで同じmやnという名前を使っていても、それは違うものとして扱われます。これを局所変数といいます。もし、関数の中の途中の計算をするために局所変数を使いたい場合は、関数定義の引数のところに記述するようにします。
{
	...
	m=1
	n=2
	i=3
	x=max(m,n)
	print(m,n,i)
	...
}

function max(m,n,	i){
	m=4
	n=5
	i=6
	...
}
"print(m,n,i)"では"1 2 3"が表示されます。これにより、「考えてもいなかったところで、同じ名前の変数を使っていたために、プログラムがうまく動作しない」という種類のトラブルを回避することができます。
 局所変数として扱わなければ広域変数となりますから、プログラムのどこからでも参照でき、また代入できます。
 関数の定義はどこに書いても構いません。実際は、このような「役に立ちそうな関数」は、ライブラリとして別ファイルに用意しておき、プログラム実行時に結合するのがうまい方法です。

6. 表示の工夫

 アクションの中で一番重要な役割を果たすのが、print文またはprintf文です。これまでに示した例でも必要な情報は得られるのですが、必ずしも見やすい表示にはなっていません。そこで、ここでは表示の工夫を考えてみます。また、データファイルの作成や印刷についても触れます。

6.1 print文

 例を2つ示します。

例18:

以下のプログラムを貼り付けたら、アイドル?.txtの内容をテキストコピーして、Copalのをクリックする。

$4>=48{print($1,$4)}
大沢逸美 58
杉田かおる 50
河合奈保子 48
浅野ゆう子 50
東ちづる 48
例19:

以下のプログラムを貼り付けたら、アイドル?.txtの内容をテキストコピーして、Copalのをクリックする。

$4>=48{print($1 "の体重は" $4 "kgです.")}
大沢逸美の体重は58kgです.
杉田かおるの体重は50kgです.
河合奈保子の体重は48kgです.
浅野ゆう子の体重は50kgです.
東ちづるの体重は48kgです.
 例18では、$1と$4の間は','で区切られています。例19では、'$1'と""の体重は""の間や、その後の'$4'との間はスペースで区切られています。表示結果でわかるように、','で区切った場合はスペースが入り、スペースで区切った場合は詰めて表示されます。前者は、出力のフィールドの区切り文字を決める組み込み変数OFSの値が用いられます(はじめはスペースになっているが変更可能)。後者は、文字列の連接と呼ばれます。
 ローレル指数を計算する例を示します。
ローレル指数 = 体重 ÷ (身長の3乗) × (10の7乗)
です。プログラムは次のとおりです。(身長の3乗)は"$3^3"、(10の7乗)は"1e+7"と書きます。
例20:
BEGIN{OFS="\t"}
{
	x=int($4*1e+7/$3^3);
	if(x>=160)	mes="肥りすぎ";
	else if(x>=145)	mes="肥りぎみ";
	else if(x>=115)	mes="正常";
	else if(x>=100)	mes="やせぎみ";
	else 		mes="やせすぎ";
	print($1,x,mes "です.");
}

実行結果は次のとおりです。

南野陽子	101	やせぎみです.
相原勇	105	やせぎみです.
堀ちえみ	118	正常です.
若村麻由美	103	やせぎみです.
早見優	106	やせぎみです.
森尾由美	108	やせぎみです.
松本明子	122	正常です.
大沢逸美	122	正常です.
小泉今日子	112	やせぎみです.
三田寛子	109	やせぎみです.
柏原芳恵	116	正常です.
中森明菜	114	やせぎみです.
松本伊代	105	やせぎみです.
沢口靖子	114	やせぎみです.
小林聡美	120	正常です.
杉田かおる	129	正常です.
荻野目慶子	119	正常です.
伊藤麻衣子	120	正常です.
高樹沙耶	108	やせぎみです.
河合奈保子	119	正常です.
今井美樹	92	やせすぎです.
香坂みゆき	113	やせぎみです.
松田聖子	99	やせすぎです.
斉藤慶子	103	やせぎみです.
岩崎良美	114	やせぎみです.
手塚理美	104	やせぎみです.
黒木瞳	106	やせぎみです.
浅野ゆう子	107	やせぎみです.
東ちづる	112	やせぎみです.

 BEGINのところで、表示のフィールド区切り文字をタブに変更しています。また、単に計算結果を表示するだけではなく、その値に応じてメッセージを表示するようにしました。
 このように、条件によって異なる処理を行うようなプログラムではif文を用います。

6.2 printf文

 もう少し表示に工夫をしたい場合はprintf文を用います。
 標準体重と肥満度の計算をしてみましょう。

標準体重=( 身長 - 100 ) × 0.9
肥満度={ 実測体重 - 標準体重} ÷ 標準体重 × 100
です。プログラムは次のとおりです。
例21:
BEGIN{ printf("%-10s%10s%10s%12s%9s\n\n","名前","身長","体重","標準体重","肥満度") }
{ printf("%-10s%8dcm%8dkg%8dkg%10.1f%%\n",$1,$3,$4,std=($3-100)*0.9,($4-std)/std*100) }
実行結果は次のようになります。
名前            身長      体重    標準体重   肥満度

南野陽子       162cm      43kg      55kg     -22.9%
相原勇         156cm      40kg      50kg     -20.6%
堀ちえみ       156cm      45kg      50kg     -10.7%
若村麻由美     163cm      45kg      56kg     -20.6%
早見優         164cm      47kg      57kg     -18.4%
森尾由美       162cm      46kg      55kg     -17.6%
松本明子       152cm      43kg      46kg      -8.1%
大沢逸美       168cm      58kg      61kg      -5.2%
小泉今日子     155cm      42kg      49kg     -15.2%
三田寛子       160cm      45kg      54kg     -16.7%
柏原芳恵       157cm      45kg      51kg     -12.3%
中森明菜       160cm      47kg      54kg     -13.0%
松本伊代       157cm      41kg      51kg     -20.1%
沢口靖子       159cm      46kg      53kg     -13.4%
小林聡美       155cm      45kg      49kg      -9.1%
杉田かおる     157cm      50kg      51kg      -2.5%
荻野目慶子     152cm      42kg      46kg     -10.3%
伊藤麻衣子     155cm      45kg      49kg      -9.1%
高樹沙耶       162cm      46kg      55kg     -17.6%
河合奈保子     159cm      48kg      53kg      -9.6%
今井美樹       168cm      44kg      61kg     -28.1%
香坂みゆき     157cm      44kg      51kg     -14.2%
松田聖子       159cm      40kg      53kg     -24.7%
斉藤慶子       163cm      45kg      56kg     -20.6%
岩崎良美       158cm      45kg      52kg     -13.8%
手塚理美       160cm      43kg      54kg     -20.4%
黒木瞳         163cm      46kg      56kg     -18.9%
浅野ゆう子     167cm      50kg      60kg     -17.1%
東ちづる       162cm      48kg      55kg     -14.0%
これまでの表示に比べると随分「見栄え」がすると思います。このプログラムもたった2つの文から成っています。プログラムの2行目の部分について説明します。
 この文は、
printf(書式,式の並び)
という形をしています。
 書式のはじめの"%-10s"は、はじめの式($1)を、文字とみなして(s)、10桁分の場所をとり、左に詰めて(-)表示することを意味しています。
 次の"%8d"は2番目の式$3を10進整数とみなして(d)、右に詰めて表示することを意味しています。
 その次の"cm"はそのまま表示されます。
 以下同様ですが、  最後の式($4-std)/std*100の表示書式"%10.1f"は、10桁分の場所に小数点以下1桁で(.1)実数表示(f)することを意味しています。その後の"%%"は'%'そのものの表示を意味し、'\n'は改行を意味しています。printfでは特に指示しない限り改行は行われません。
 付録Aにprintfで指定できる書式をまとめてあります。
 printf文を使うときれいに揃った表示が得られるのですが、表示の目的は結果を知ることにあるわけですから、print文で十分な場合が多いものです。単に見栄えのために無駄な努力をすることはありません。

←戻る

付録A. AWKのまとめ

□ AWKプログラム

 「AWKプログラム」は、「パターン−アクション規則」と「関数定義」の並びである。
 
パターン{アクション}
パターン{アクション}
...
function 名前(引数の並び){ 文 }
function 名前(引数の並び){ 文 }
...
 
パターン−アクション規則や関数定義の前後に空行を挿入してもよい。関数定義はどこに記述してもよい。
 入力レコードをひとつ読む度に、パターン−アクション規則のパターンが順に検査される。パターン−アクション規則は、パターンが真となるレコードに対して、対応するアクションが実行される。
 パターンあるいは{アクション}のいずれかを省略することができる。パターンを省略するとすべてのレコードにアクションが実行され、{アクション}を省略するとパターンが真となるレコードを表示する。

←戻る

□ パターン

 「パターン」は次のいずれかである。

□ アクションと文

 「アクション」は文の並びである。
 文は次のいずれかである。

文は改行か';'で区切る。';'が単独で用いられると、空文を表す。文の前後には空行を挿入してもよい。
 長い文は、行末に'\'を置いて次行に続けることができる。", { && || do else"のあと、および"if( )","for( )"のあとは、'\'を置かずに改行しても継続とみなされる。

←戻る

□ 制御文

 制御文は次のいずれかである。

if-else文において、最初の文がelseと同じ行にある場合、この文は';'で終了するか"{ }"で囲まなければならない。
 基本的な制御構造については付録Bに示した。

←戻る

□ 入出力文

 入出力文は次のとおりである。

getline
入力レコードを"$0"にセット
getline < file
fileの次のレコードを"$0"にセット
getline(変数)
次の入力レコードを変数にセット.(NRとFNRもセット)
getline(変数)<file
fileの次のレコードを変数にセット
print
現在レコードを表示
print(式)
式を表示
printf(書式, 式)
式を書式に従って表示
system(コマンド)
MS-DOSのコマンドを実行
'(',')'は省略できる。
 "getline"はファイルを読み終わると0、エラーのときは-1を返す。

←戻る

□ 式

 式は次のいずれかである。
数値定数
文字列定数"......"
変数アルファベット数値か文字列で始まる文字列
 
関数呼び出し関数名(式の並び)関数の戻り値
 
代入式=式代入した値
代入式+=式代入した値
代入式-=式代入した値
代入式*=式代入した値
代入式/=式代入した値
代入式%=式代入した値
代入式^=式代入した値
インクリメント++式式+1
インクリメント式++
デクリメント--式式-1
デクリメント式--
 
条件式式1 ? 式2 : 式3式2または式3
論理和式||式0または1
論理積式&&式0または1
比較式<式0または1
比較式<=式0または1
比較式>式0または1
比較式>=式0または1
比較式!=式0または1
比較式==式0または1
 
配列要素式 in 式0または1
 
パターンの検査式~/正規表現/0または1
パターンの検査式!~/正規表現/0または1
 
式+式演算結果
式-式演算結果
式*式演算結果
式/式演算結果
剰余式%式演算結果
累乗式^式演算結果
 
+式
-式
(式)

←戻る

□組み込み変数

 次の組み込み変数が用意されている。

ARGC
コマンド行の引数の数
ARGV
コマンド行の引数の配列
FILENAME
現在の入力ファイル名
ENVIRON["..."]
環境変数の値
FS
入力のフィールドセパレータ(はじめはスペースまたはタブ)
RS
入力のレコードセパレータ(はじめは改行)
NF
現在レコードのフィールド数
NR
現在の通算レコード
FNR
現在の入力ファイルの通算レコード
OFS
表示のフィールドセパレータ(はじめはスペース)
ORS
表示のレコードセパレータ(はじめは改行)
OFMT
数の表示のフォーマット(はじめは"%.6g")
RSTART
matchでマッチした文字列の開始位置
RLENGTH
matchでマッチした文字列の長さ
$0
現在の入力レコード
$1,...,$NF
第1フィールド,...,第NFフィールド

□ 組み込み文字列関数

gsub(r,s,t)
文字列tの中に現れる文字列rをすべて文字列sで置換する.置換した数を返す.tを省略すると$0が使われる.
index(s,t)
文字列sの中の文字列tの位置を返す.tが現れない場合は0.
jindex(s,t)
日本語文字列sの中の文字列tの位置を返す.tが現れない場合は0.
length(s)
文字列sの長さを返す.
jlength(s)
日本語文字列sの長さを返す.
match(s,r)
文字列sが文字列rにマッチする位置を返す.マッチしないときは0.
split(s,a,fs)
fsをフィールドセパレータとして文字列sを配列aに分解し、フィールド数を返す.
sprintf(書式,式)
書式で整えた式の並びを返す.
sub(r,s,t)
gsub()と同様.ただしはじめの1回だけ置換する.
substr(s,i,n)
文字列sのi番目から始まるn文字を返す.
jsubstr(s,i,n)
日本語文字列sのi番目から始まるn文字を返す.
←戻る

□ 組み込み算術関数

atan2(y,x) atan(y/x)で-π〜πの値. sin(x) sin関数 cos(x) cos関数 exp(x) exp関数 log(x) 自然対数 sqrt(x) 平方根 int(x) 小数点以下を切り捨て rand() 疑似乱数 0以上1未満 srand() 乱数の初期化
←戻る

□ 書式変換

 printfとsprintfの中では次のような変換が利用できる。ここでは例で示す。
printf("|%c|",65)|A|
printf("|%d|",65)|65|
printf("|%5d|",65)| 65|
printf("|%05d|",65)|00065|
printf("|%f|",65)|65.000000|
printf("|%5.1f|",65)| 65.0|
printf("|%e|",65)|6.500000e+01|
printf("|%5.1e|",65)|6.5e+01|
printf("|%g|",65)|65|
printf("|%o|",65)|101|
printf("|%s|","hirosaki")|hirosaki|
printf("|%10s|","hirosaki")| hirosaki|
printf("|%-10s|","hirosaki")|hirosaki |
printf("|%.4s|","hirosaki")|hiro|
printf("|%10.4s|","hirosaki")| hiro|
printf("|%-10.4s|","hirosaki")|hiro |
printf("|%%|")|%|
←戻る

□ 正規表現

 例で正規表現の記法を示す。通常これらの組み合わせとなる。
A
Aそのもの(以下の特殊文字を除く普通の文字)  
\\
\そのもの
\"
"そのもの
\t
タブ
\n
改行
\f
フォームフィード
\b
バックスペース
\033
8進数033  
^a
文字列の先頭が'a'
a$
文字列の末尾が'a'  
.
任意の1文字
[abc]
'a''b''c'のどれか1文字
[^abc]
'a''b''c'以外の1文字
[a-e]
'a''b''c''d''e'のどれか1文字
[^a-e]
'a''b''c''d''e'以外の1文字
a*
0個以上の'a'の並び
a+
1個以上の'a'の並び
a?
'a'が1個あるいは0個
abc|de
"abc"または"de"
←戻る

付録B. プログラムの制御構造

 AWKの文を記述する際の制御構造を示す。これらの制御構造は多くのプログラミング言語に共通している。

逐次構造(文の並び):
{文1; 文2; ... 文n}
選択構造(if文):
if(式) 文
選択構造(if-else文):
if(式) 文1 else 文2
ループ構造(for文):
for(式1; 式2; 式3) 文
ループ構造(while文):
while(式) 文
ループ構造(do-while文):
do 文 while(式)
←戻る

課題の解答

課題2:の解答
$4<=40
$4<=40{print($0)}
$4<=40{print}
←戻る
課題3:の解答
($4<=40 && $3<=160){print($0)}
←戻る
課題4:の解答
NR==7,NR==12{print(NR,$1)}
←戻る
課題5:の解答
$2=="兵庫"{print($1)}
←戻る
課題6:の解答
$1~/子$/{print($1)}
←戻る
課題7:の解答
$1~/[大中小]/{print($1)}
←戻る
課題8:の解答
{
	s[$3]++;
	t[$4]++;
}
END{
	for(i in s){print(i,"\tcm\t",s[i])};
	for(i in t){print(i,"\tkg\t",t[i])};
}

出力をコピーし、エクセルを起動して貼り付け、第1キーをB、第2キーをAで並べ替え、身長・体重それぞれを範囲指定してグラフを描いてみよう。

←戻る
課題9:の解答
BEGIN{
	pi=3.14159;
	r=10;
	chokkei=r*2;
	enshuu=2*pi*r;
	menseki=pi*r*r;
	print "半径=",r,"cm";
	print "直径=",chokkei,"cm";
	print "円周=",enshuu,"cm";
	print "面積=",menseki,"cu";
}
BEGIN{
	pi=3.14159;
	r=10;
	print "半径=",r,"cm";
	print "直径=",r*2,"cm";
	print "円周=",r*2*pi,"cm";
	print "面積=",r*r*pi,"cu";
}
←戻る