これは強力! AWKとパイプの新しい関係 ~ 時刻を取得する関数、Socket通信、双方向パイプ

CodeZine / 2014年12月10日 14時0分

 従来のAWKではパイプ処理で得られる情報は他のコマンドに任せようという発想でしたが、本格的に使う場合にはAWKそのもので処理できるとプログラムの記述が統一され分かりやすくなります。そうした中で最初に拡張されたものは時刻の取得に関する関数群でした。今回は時刻を取得する関数から、Socket通信や双方向パイプまで盛りだくさんの内容でGNU拡張された関数を解説していきます。

■時刻・時間を取得する

 GNU AWK(以下、gawk)は数多くのGNU拡張がなされており、その拡張の中で最初に導入されたのが時間・時刻に関する関数群です。今まではAWK 単独でUnix時間(1970年1月1日00:00からの経過秒数)をsrand()関数で裏ワザ的に取得するか、dateコマンドからパイプで受け取る方法しかありませんでした。

$ awk 'BEGIN{print srand() + srand()}' 1410325425 $ date +%s 1410325425 $ awk 'BEGIN{"date +%s" | getline; print $0}' 1410325425
 最初のものは乱数の種であるsrand()関数を用いたもので、最初のsrand()関数で種を初期化し、次の srand()関数でUnix時間を返すものです。ただし、srand() 関数が必ずUnix時間を返すとは限らず、gawkがコンパイルされた環境に依存するという問題があります。

 2番目のdateコマンドの"%s"というフォーマットはGNU拡張ですから、GNU拡張されていないdateコマンドでは期待した値を返さないでしょう。

 また、3番目のgetlineを長いAWKプログラムで使う場合には、"date +%s"をclose()関数でクローズする必要がありますが、AWKが終了するタイミングでクローズするため、あえて明示的にクローズしていません。

 ここで導入されたのがsystime()関数です。

$ gawk 'BEGIN{print systime()}' 1410325425
 これによりUnix時間を簡単に取得することができるようになりました。

 Unix時間が分かっても、人が理解できる時刻に変換できないと使い勝手が悪いため、導入されたのがstrftime()関数です。strftime()関数はフォーマットとUnix時間を引数として変換を行います。

$ gawk 'BEGIN{print strftime("%Y/%m/%d %H:%M:%S",1410325425)}' 2014/09/10 14:03:45
 つまり、先ほどのsystime()関数と組み合わせれば、現在の時刻を人が理解できる形式で取得できます。

$ gawk 'BEGIN{print strftime("%Y/%m/%d %H:%M:%S",systime())}' 2014/09/10 14:03:45
 次は逆算、つまり、人が理解できる時刻からUnix 時間に変換することが求められます。

 しばらくの間、人が理解できる時刻からUnix時間への変換はAWKの関数を駆使すれば求められるため導入が遅れていましたが、現在はmktime()関数として導入されています。

$ gawk 'BEGIN{print mktime("2014 09 10 14 03 45")}' 1410325425
 mktime()関数の引数は文字列として与えられ、"YYYY MM DD HH MM SS"という形式となっています。

 他の言語と仕様が異なる点としては、例えば、秒に59以上の値を入れても認識してくれることが挙げられます。

$ gawk 'BEGIN{print mktime("2014 09 10 14 03 90")}' 1410325470
■置換の拡張

 次に拡張されたのが置換に関するものです。

 置換を行う関数にはsub()関数とgsub()関数がありますが、共に戻り値が置換した個数であることや、元々の文字列を破壊的に置換することに対して、不便を感じていた人もいると思います。

 加えて、従来のAWKには後方参照による置換が行えず、他の言語と比較しても便利なものとはいえませんでした。

 特に、与えられた文字列を破壊せずに新たな変数に代入する場合には、次のようにいったん一時変数に代入する工夫が必要でした。

$ echo "sumomomomomomomomonouti" |\ > awk '{tmp=$0;gsub(/m/, "M", tmp);print tmp; print $0}' suMoMoMoMoMoMoMoMonouti sumomomomomomomomonouti
 そこで拡張された関数がgensub()関数です。

 gensub()関数は置換対象文字列を直接置換するのではなく、置換された文字列を返します。また、後方参照による置換も可能にしています。

$ echo "sumomomomomomomomonouti" |\ > gawk '{print gensub(/m/, "M", "g", $0); print $0}' suMoMoMoMoMoMoMoMonouti sumomomomomomomomonouti
 gensub()関数は4つの引数をとります。最初の2つはsub()関数やgsub()関数と同じですが、3番目の引数は"1"であれば最初に出現した対象となる正規表現の置換を行い、"g"であれば全ての対象となる正規表現の置換を行います。

 最後の引数は対象文字列を示しています。

 なお、後方参照とは、正規表現による検索文字列のうち、丸括弧で括りグループとした部分にマッチした文字列を、置換文字列の中で\\1, \\2のようにして参照できる機能です。sedをはじめ他の言語でも似たような記述で後方参照ができます。参考のためにsedコマンドでの例も挙げておきます。

$ echo "sumomomomomomomomonouti" |\ > gawk '{print gensub(/(mo)(no)/, "\\1\"\\2\"", "g", $0)}' sumomomomomomomomo"no"uti $ echo "sumomomomomomomomonouti" |\ > sed 's/\(mo\)\(no\)/\1"\2"/g' sumomomomomomomomo"no"uti
 グループにマッチした文字列を後方参照するには、\\(バックスラッシュ2文字)に数字を添えます。特にgensub()関数はシェル芸でも時々使われる関数の1つなので、覚えておくと良いでしょう。



CodeZine

トピックスRSS

ランキング