Socket通信
従来のAWKでは、基本的にシステムにマウントされているファイルシステム上のファイルのみを扱うという発想でしたが、インターネット上のファイルも扱えるようにするためにSocket通信が導入されました。
決して簡単に記述できるわけではないので、シェル芸の中でお目にかかったことはありませんし、シェル芸で用いるのであれば、curlコマンドやwgetコマンドの方が楽かもしれません。
例えば、筆者が運営する「AWK Users JP」のトップページをダウンロードするプログラムは次のようになります。
BEGIN {
ORS = "\r\n";
base_url = "gauc.no-ip.org";
port = 80;
service = "/inet/tcp/0/" base_url "/" port;
request = "GET /awk-users-jp/ HTTP/1.0";
print request |& service;
print "" |& service;
while ((service |& getline) > 0) {
print $0;
}
close(service);
}
【プログラムの説明】
- HTTPの改行文字は"\r\n"ですので、組込変数ORSを変更します。
- 変数base_urlに取得するURLのドメイン、変数portにHTTPのポートである80を代入します。
- 変数requestにHTTPでのリクエスト文を代入します。
- 変数serviceに「/inet/tcp/<PORT1>/<PROXY>/<PORT2>」という形で通信先を代入します。<PORT1>にはListenするポートを指定します(クライアントの場合には0)。<PROXY>には、ProxyがあればProxyのドメインを指定しますが、Proxyを介さない場合には取得先のドメインを指定します。<PORT2>には相手のポートを指定します。とても変な記法ですが、bashのSocket通信(bashのSocket通信はListenできません)にも似ています。
- GNU拡張された双方向パイプ"|&"(後述します)を用いて、リクエストを変数serviceに渡します。
- 終端を示す改行文字を変数serviceに渡します。
- while文を用いてサービスから双方向パイプから得られる文字列を表示します。
- 最後に変数serviceをクローズします。
gawkで記述することが面倒なことが分かると思いますし、Socketそのものを扱うので、HTTPなどの通信知識が必要になります。
ファイル名をget.awkとして保存し、実行してみますと、「AWK Users JP」のページが表示されたと思います。
$ gawk -f get.awk (「AWK Users JP」のHTMLが表示される)
Socket通信が使えるということは、gawkならWebサーバーも構築できるということです。しかし、一般的なHTMLにはHTMLだけでなくCSS、JavaScript、画像などのファイルも同時にリクエストされますが、今まで説明した範囲ではHTMLしかレスポンスとして返さないので、実用的なWebサーバーを構築することができません。
この問題はforkが使うことで解決できますので、機会があれば説明したいと思います。
双方向パイプ
先ほどのSocket通信の例でも用いましたが、従来のAWKにはないパイプとして双方向パイプというものがあります。これを使うと様々なことができます。
この双方向パイプは、Shellの世界で言えば「名前付きパイプ」に近い処理です。双方向パイプには"|&"という見慣れない記号を用います。このパイプ先にリダイレクトを行うのですが、リダイレクト先の処理が完了するのを待ちます。その後、再びリダイレクト先から双方向パイプ|& を通してgetline文で読み込みます。この際にgetline文はclose()関数でクローズする癖をつけておきましょう。
つまり、一度パイプ先のサービスやコマンドに処理を渡しておいて、その処理が終わった後で必要になったら取り出すという仕組みになっています。
少し複雑な例として、双方向パイプを用いたsortコマンドによるフィールドのソート例を挙げておきます。
BEGIN {
cmd = "sort";
}
{
for(i = 1; i <= NF; i++){
print $i |& cmd;
}
close(cmd, "to");
str = "";
while(cmd |& getline > 0){
str = str " " $0;
}
close(cmd);
sub(/^[ ]/, "", str);
print str;
}
Socket通信の場合には"\r\n"だけが返されるとリクエスト終了の意味を示すため、明示的なクローズは不要でしたが、上記の例の双方向パイプを用いた処理の場合にはclose()関数に第2引数が増え、"to"と"from"という指定を行うことができます。
上の例では双方向パイプ先のsortコマンドはずっと待ち続けているので、"to"を外すと処理が終わりません。これは、sortコマンドの入力の終端がファイルの終わりであるためです。close()関数で入力の終端を明示しないと、sortコマンドが入力の終了を認識してくれません。
そこで、"to"を付けることでsortコマンドから見た際の入力、AWKから見たときに出力(なので"to") をクローズする必要があります。
変数cmd、つまりsortコマンドの結果はwhile文で読み出しています。
先ほどのプログラムをline_sort.awkとして保存し、実行してみましょう。
$ seq 1 10 | xargs | rev | gawk -f line_sort.awk 01 1 2 3 4 5 6 7 8 9
上のコマンドでは、ちょっとしたシェル芸で書いています。
seqコマンドにより1から10までを出力します。seqコマンドがインストールされていない場合にはjotコマンドで代用できるようです。
xargsコマンドは本来コマンドへ渡す引数を生成するコマンドですが、単独で用いることで列を行に変換することができます。シェル芸では本来の意味と異なる目的で使われるコマンドNo.1です。
revコマンドは文字列を逆にするコマンドです。revコマンドは便利なのですが、インストールされていない場合もありますので注意してください。
このようにして得られた文字列を行単位でソートしていきます。
revコマンドで10という数字が01に置き換わってしまっているため結果が不自然ですが、正しい結果を返しています。
シェル芸を行う際の基本ですが、以下のように個々にパイプ単位で実行していくと、その流れが分かるでしょう。
$ seq 1 10 | xargs 1 2 3 4 5 6 7 8 9 10 $ seq 1 10 | xargs | rev 01 9 8 7 6 5 4 3 2 1 $ seq 1 10 | xargs | rev | gawk -f line_sort.awk 01 1 2 3 4 5 6 7 8 9
今回のまとめ
今回はSocket通信と双方向パイプというGNU 拡張を説明したため複雑になってしまいましたが、gawkを120%使うには必要な知識になります。
とはいえ、紙面だけでは全て説明することは難しく、どこかでAWKの勉強会を行いたいと考えています。
ひょっとしたら、本記事が公開されるころには勉強会を開始しているかもしれませんが、ご意見がありましたら筆者のTwitterアカウントの@hi_saitoまでご連絡ください。
『シェルスクリプトマガジン 2014 December(Vol.20)』は、ただいま絶賛発売中!

『シェルスクリプトマガジン』は日本で唯一のシェルスクリプト総合誌(毎月25日発売)。最新号 2014 December(Vol.20)はクリスマス全開の表紙が目印!特集は「エンジニアになりたい!新人の為のUNIX基礎講座」。そもそもパソコンってなんなのか?いまさら聞けなき基礎知識を、USPエンジニアがわかりやすく解説。大岩先生の「人間とコンピュータの可能性」では、エンジニアの抽象化能力に着目。菅さんの「中小企業手作りIT化奮戦記」では仮想化環境の導入でサーバの集約に挑みます。 本連載「GNU AWK処方箋」の第3回はGNU AWKで新しく導入された組込変数FPAT(フィールドパターン)について解説します。他にも「ユニケージ開発手法 コードレビュー」や「組織文化を変える汗と涙の物語 アジャイル改善塾」など好評連載中です!
► お求めは、Amazon.co.jp、USP研究所のWebサイト、取り扱い書店まで。
シェルスクリプトマガジン 2014 December(Vol.20)
価格:500円+税
仕様:B5版 64ページ オールカラー
発行人:ユニバーサル・シェル・プログラミング研究所
