02 月 09 日(木)
AWK をより効率的に記述し,より高度に活用しよう.
AWK の第一歩 や gawk 日本語マニュアル 等を参考にしながら, 作業を進めて行こう.
これまでは,入出力として, コマンドラインに指定されたデータファイルからの入力と 標準出力への出力しか利用してこなかった. しかし,AWK の入出力機能はこれらだけに限られている訳ではない. シェルやC言語と同様に, ファイル・標準入出力・コマンドとの間の入出力機能を備えている:
{ print $0 | "sort -n" } # 数値データを昇順に並べ替える
詳しくは, マニュアルの“入出力ステートメント”の項目 を参照せよ.
次の形式で,AWK でも関数を定義できる:
function 関数名(引数1, 引数2, ...) { アクション }
シェルやC言語に似ているが, 共通点・相違点に注意しよう:
たとえば,3個の仮引数からなる関数を2個の実引数だけで呼び出せる. 省略された 3番目の引数の値は空文字列または 0 となる.
詳しくは, AWK オンラインマニュアルの“関数”の項目 を参照せよ.
AWK には,C言語のライブラリ関数のような組み込み関数もある. 詳しくは, AWK オンラインマニュアルの“数値関数”,“文字列関数”, および“時間関数”の項目 を参照せよ.
簡単な例として,sed のような AWK スクリプト sed.awk を作成してみよう:
#!/usr/bin/awk -f
# 説明:sed のような AWK スクリプト
# 使い方:sed.awk r='正規表現' s='置換文字列' ファイル名 ...
{
sub(r, s, $0) # レコード $0 中の正規表現 r を 文字列 s に置換
print $0
}
このスクリプトの使用例は次の通り:
$ ./sed.awk r='#.*' s='' sed.awk
これは,次のコマンドと同じ結果となるハズ:
$ sed 's/#.*//' sed.awk
ちなみに,この例では, スクリプトからコメント(各行の # 以降の文字列)を除去している.
なお,sed については, 以前のページ や sed オンラインマニュアル を参照せよ.
次の例では,sed.awk を改造し,ユーザ関数を定義してみた:
#!/usr/bin/awk -f # 説明:sed のような AWK スクリプト # 使い方:sed.awk r='正規表現' s='置換文字列' ファイル名 ... function sed(reg, str) { sub(reg, str, $0); print $0; } { sed(r, s); }
なお,この例では,関数化によってスクリプトがかえって長くなってしまったが, 何をするスクリプトなのか一目瞭然(いちもくりょうぜん)になった.
今回の課題については,任意提出とする. 未提出でも減点とはしないが, 特に,成績を挽回したい者は取り組むこと.
上に紹介した sed.awk では,コマンドラインの記述がちょっと面倒臭い. 本来の sed に近いコマンドライン形式となるように改良せよ.
レポートには,スクリプトとデータだけでなく, どのように改良したのかも説明すること.
アドバイス:
$ ./sed.awk s='検索パターン/置換パターン' 入力ファイル ...
パターン文字列 s を記号 / で分割し, 前半部分を検索パターン, 後半部分を置換パターンとすればよい.
なお,コマンドラインで指定された変数の初期化は, BEGIN ブロックの直後に実行されることに注意しよう. したがって,パターン文字列の分割処理は, 第1レコードの入力直後に1度だけ実行する必要がある. つまり,NR == 1 のブロックの先頭で行えばよい. (すべてのレコードで分割するのは非効率.)
$ ./sed.awk e='s/検索パターン/置換パターン/' 入力ファイル ... とか $ ./sed.awk 's/検索パターン/置換パターン/' 入力ファイル ...
前者の形式では,分割結果の配列要素が増えるだけだ. 後者の形式では, 組み込み変数 ARGV や 配列操作命令 delete などを利用すればよい.