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に取り込まれている基本的なコマンドは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自体が便利なコマンドなので、こうした違いをしっかり把握しておきたい。
