MPFRによる拡張
GNU AWKをソースからビルドする際、GNU MPFRとGNU MPがすでに導入されている場合には、自動的にMPFR拡張が導入されます。導入されているかどうかは、gawkのバージョンを表示することで分かります。次のように表示されたバージョンは、この拡張が組み込まれています。
$ gawk --version GNU Awk 4.1.60, API: 1.1 (GNU MPFR 3.1.2, GNU MP 5.1.2)
この拡張が組み込まれていない場合には、「(GNU MPFR 3.1.2, GNU MP 5.1.2)」という部分が含まれません。ここでは、この拡張が組み込まれているとして進めていきます。
最も簡単にMPFRによる拡張機能を用いる方法は、gawkの起動オプションで-Mを指定することです。
このMPFRによる拡張機能が組み込まれていないgawkでは53ビットまでの数値を扱えます。これは、次のような例で試すことができます。
$ echo 53 | gawk '{print 2 ^ $1 - 1}' 9007199254740991 $ echo 54 | gawk '{print 2 ^ $1 - 1}' 18014398509481984
2のべき乗は必ず偶数になるため、これから1を引いた数は奇数になるはずですが、2 ^ 54 - 1が偶数になっていることが分かります。これはgawkが53ビット演算までしか正確に計算できていないということです。そこで今度はMPFRを用いた拡張を試してみましょう。
$ echo 54 | gawk -M '{print 2 ^ $1 - 1}' 18014398509481983
検算はbcコマンドで行ってみましょう。
$ echo '2^54-1' | bc 18014398509481983
このように正しく演算を行うことが可能になります。
また同様に、従来のgawkでは非常に小さな数は0として出力されていましたが、MPFRの拡張を用いることで、そのままの数値で扱うことも可能になります。
$ gawk 'BEGIN {x = 1.0e-400; print x + 0}' 0 $ gawk -M 'BEGIN {x = 1.0e-400; print x + 0}' 1e-400
AWKでls
AWKはシェルを介した他のコマンドとの相性が良いため、「シェル芸勉強会」の中でも毎回用いられるコマンドの1つです。しかし、基本的なコマンドほどOS間で出力形式が微妙に異なるため、悩まされたことも多いのではないかと思います。そこで、lsコマンドそのものを作ってみます。
@load "filefuncs" BEGIN { passwd = "/etc/passwd"; group = "/etc/group"; FS = ":"; while (getline < passwd > 0) { user_of[$3] = $1; } close(passwd); while (getline < group > 0) { group_of[$3] = $1; } close(group); for (i = 1; i <= ARGC - 1; i++) { stat(ARGV[i], stat_data_of); m_time = strftime("%Y-%m-%d %H:%M", stat_data_of["mtime"]); printf("%s %3d %8s %8s %9d %s %s\n", stat_data_of["pmode"], stat_data_of["nlink"], user_of[stat_data_of["uid"]], group_of[stat_data_of["gid"]], stat_data_of["size"], m_time, stat_data_of["name"]); } }
ここで初めて登場した拡張@loadが最初の行にありますが、この@loadにより様々な拡張を行うことができます。ここでは「filefuncs」という拡張を呼び出しています。このfilefuncsによりファイル情報を直接読むことができます。
このfilefuncsではstat()関数が加えられ、このstat()関数の第1引数のファイルの情報が、第2引数の配列に格納されます。また、フォーマットをlsコマンドらしくするため、/etc/passwdファイルと/etc/groupファイルの中身を調べてuidとgidを取得しています。
では、実行してみましょう。
$ touch a b $ gawk -f ls.awk a b -rw-r--r-- 1 hi_saito hi_saito 0 2014-11-03 15:18 a -rw-r--r-- 1 hi_saito hi_saito 0 2014-11-03 15:18 b $ ls -l --time-style=long-iso a b -rw-r--r-- 1 hi_saito hi_saito 0 2014-11-03 15:18 b -rw-r--r-- 1 hi_saito hi_saito 0 2014-11-03 15:18 a
このようにGNUのcoreutilsのlsコマンド(--timestyle=long-isoオプション相当)に近い出力を得ることができました。