awkの実装の違いを知る

前回説明したように、Macに標準で搭載されているawkは「nawk」、Linuxディストリビューションに標準で搭載されているawkは「GNU awk」だ。nawkは最もオリジナルに近いawkの実装系で、AWK開発者らがまとめた書籍で説明されている機能がほぼそのまま実装されている。ほかの実装系との互換性も一番高いawkだ。

一方、Linuxディストリビューションに標準でデプロイされていることが多いawkコマンドはGNU awkだ。こちらはオリジナルのawkコマンドとの互換性は保ちつつ、さまざまな拡張機能が追加されている。具体的にはプログラミング言語としての機能が強化されており、GNU awkだけでさまざまなことができるようになっている。

こうした違いがあるので、Linuxのawkコマンドを使って開発していたスクリプトがMacでは動かない、といったような事態が起き得る。知っていればこの辺りで苦労することもなくなるので、今回はこの辺りについて簡単にまとめておこう。

オリジナルの機能

正確に全体を網羅したものではないが、AWK本に記載されていて実装系ごとの互換性が確保されている機能についてざっとまとめておく。まず、awkの基本的なプログラミングは次の形式で成り立っている。

パターン-アクションステートメント
パターン { アクション }

アクション部分は次のような記述ができる。

アクション
if (式) ステートメント [ else ステートメント ]
while (式) ステートメント
for (式; 式; 式) ステートメント
for (変数 in 配列) ステートメント
do ステートメント while (式)
break
continue
{ [ ステートメント ... ] }
print [ 式-list ] [ > 式 ]
printf フォーマット [ , 式-list ] [ > 式 ]
return [ 式 ]
next
nextfile
delete array[ 式 ]
delete array
exit [ 式 ]

提供されている関数は次の通りだ。

種類 関数
算術演算 atan2(y, x)
算術演算 cos(x)
算術演算 exp(x)
算術演算 int(x)
算術演算 log(x)
算術演算 rand()
算術演算 sin(x)
算術演算 sqrt(x)
算術演算 srand(expr)
文字列操作 gsub(r, t, s)
文字列操作 index(s, t)
文字列操作 length(s)
文字列操作 match(s, r)
文字列操作 split(s, a, fs)
文字列操作 sprintf(fmt, expr, ...)
文字列操作 sub(r, t, s)
文字列操作 substr(s, m, n)
文字列操作 tolower(str)
文字列操作 toupper(str)
入出力ほか close(expr)
入出力ほか コマンド | getline [変数]
入出力ほか getline
入出力ほか getline 変数
入出力ほか getline [変数]
入出力ほか system(コマンド)

提供されている変数は次の通りだ。

特殊変数 内容
ARGC 引数の数
ARGV 引数の配列
CONVFMT 数字を変換するときのフォーマット(デフォルト "%.6g")
ENVIRON 環境変数の配列
FILENAME 入力ファイルの名前
FNR 現在のファイルにおける現在のレコードの順序番号
FS 正規表現で表記されるフィールドセパレータ
NF 現在のレコードのフィールド数
NR 現在のレコードの順序番号
OFMT 数字の出力フォーマット(デフォルト "%.6g")
OFS 出力フィールドセパレータ(デフォルト ブランク)
ORS 出力レコードセパレータ(デフォルト 改行)
RLENGTH match()関数で一致した文字列の長さ
RS 入力レコードセパレータ(デフォルト 改行)
RSTART match()関数で一致し文字列の開始ポジション
SUBSEP 添字の区切りデータ

コマンドオプションも次のように限定的だ。

コマンドオプション 内容
-F 正規表現で使われる入力データフィールドセパレータを指定
-v 変数=値 指定した変数に指定した値を設定した状態でプログラムを実行。複数回指定可能
-f ファイルパス コマンドラインからではなく、指定されたファイルからプログラムを読み込み

演算子やそのほかの変数など書かないといけないことはほかにもあるのだが、それらはどの実装系でも同じで拡張されていることも少ない部分なので、とりあえず上記を基本の機能として捉えておけばよいだろう。

上記をご覧いただけばわかるように、awkで提供されている機能は現在主流のプログラミング言語と比べてとてもシンプルなものだ。テキストデータ処理に必要となる最小限かつ便利な機能が詰まったもののようなもので、コマンドとプログラミング言語の中間くらいの位置付けといってもよいかもしれない。

しかしながら、awkの提供する機能は本質的で強力だ。これらの機能を使いこなせるだけでも多くの処理を実現できる。

nawkの拡張機能

Mac nawkの拡張機能はかなり少ない。オリジナルのawkに対して次のような拡張機能が取り込まれているくらいだ。若干動作が異なる部分もあるが、基本的はこの辺りだと考えておいていただきたい。

Mac nawk 拡張機能
fflush()関数
POSIXLY_CORRECT環境変数
  • Mac awk

    Mac awk

Macに取り込まれている基本的なコマンドはFreeBSDから移植されたものだ。FreeBSDはnawkをawkの実装として採用しており、このためMacのawkもnawkだ。このFreeBSDのnawkのほうはもうちょっと拡張機能が追加されている。簡単にまとめると次のようになる。

FreeBSD nawk拡張機能
-dオプション
-Vオプション
-safeオプション
fflush()関数
compl()関数
and()関数
or()関数
xor()関数
lshift()関数
rshift()関数

コマンドオプションがいくつかと、ビット演算用の関数が追加されている。ビット演算はオリジナルにはない機能なので、ビット演算をした段階でほかのawk実装系との互換性はあまり期待できないことになる。

GNU awkの拡張機能

ではGNU awkの拡張機能を見てみよう。nawkの拡張機能と比べると、GNU awkの拡張機能はかなり多い。その全てをここで紹介するのは難しいので、代表的なところを大雑把にまとめておく。

GNU awk拡張機能
--fileオプション
--field-separatorオプション
--assignオプション
-bオプション
--characters-as-bytesオプション
-cオプション
--traditionalオプション
-Cオプション
--copyrightオプション
-dオプション
--dump-variablesオプション
-Dオプション
--debugオプション
-eオプション
--sourceオプション
-Eオプション
--execオプション
-gオプション
--gen-potオプション
-hオプション
--helpオプション
-iオプション
--includeオプション
-lオプション
--loadオプション
-Lオプション
--lintオプション
-Mオプション
--bignumオプション
-nオプション
--non-deciman-dataオプション
-Nオプション
--use-lc-numericオプション
-oオプション
--pretty-printオプション
-Oオプション
--optimizeオプション
-pオプション
--profileオプション
-Pオプション
--posixオプション
-rオプション
--re-intervalオプション
-sオプション
--no-optimizeオプション
-Sオプション
--sandboxオプション
-tオプション
--link-oldオプション
-Vオプション
--versionオプション
--オプション
@include機能
@load機能
@namespace機能
8進数定数
16進数定数
|&演算子
\xエスケープシーケンス
ローカライズ文字列
I/O拡張機能
BEGINFILE特殊パターン
ENDFILE特殊パターン
ARGIND変数
AWKPATH変数
BINMODE変数
ERRNO変数
FIELDWIDTHS変数
FPAT変数
FUNCTAB変数
IGNORECASE変数
LINT変数
PREC変数
PROCINFO変数
ROUNDMODE変数
RT変数
SYMTAB変数
TEXTDOMAIN変数
RS変数の正規表現拡張
and()関数
asort()関数
asorti()関数
bindtextdomain()関数
compl()関数
dcgettext()関数
dcngettext()関数
gensub()関数
lshift()関数
mktime()関数
or()関数
patsplit()関数
rshift()関数
strftime()関数
strtonum()関数
systime()関数
xor()関数
split()拡張動作
close()拡張動作
match()拡張動作
printf()拡張動作
sprintf()拡張動作
length()拡張動作
POSIXLY_CORRECT環境変数

このように、GNU awkには拡張機能が多い。コマンドオプションも豊富だし、@からはじまる独自の機能もある。変数、関数、パターンが新たに追加されているほか、いわゆるプログラミング言語仕様に相当する部分の機能も拡張されている。

ポイントは「別物」と捉えること

上述したように、GNU awkの機能はオリジナルのawkよりもかなり拡張されている。オリジナルのawkの機能とは互換性があるものの、拡張機能を使った場合には全くの別のコマンドのようなものだ。GNU awkの拡張機能をオリジナルの機能を置き換えることができるかと言えばそんなことはなく、基本的に拡張機能を使った時点で別物だと考えたほうがよい。

逆に、GNU awkの拡張機能を使わずにnawkの機能だけで実装できるなら、それはそれで推奨できる。互換性が高いので、ほかのawkで実行できるからだ。特にmawkの存在が大きい。awkでテキスト処理をしている場合、データサイズが大きいとそれだけで数時間かかるようなケースも出てくる。しかし、nawk互換にしておけば実装系をmawkへ入れ替えるだけで高速化が期待できる。これは大きなアドバンテージだ。GNU awkでしか動かないとこうはいかない。高速化を考えるなら、nawk互換にしておくことには意味があるのだ。

今回紹介したawkの実装系を理解しておくだけで、用途に応じて使い分け、適切な活用ができるようになる。そもそもawk自体が便利なコマンドなので、こうした違いをしっかり把握しておきたい。

参考