2009年3月25日水曜日

外部コマンドを実行し、その実行結果をパイプで受け取る方法

早い話が、perlで、open("command |");というのをLimiboでやりたいのです。
ネットで調べてもずばりの情報が無く試行錯誤しました。
間違っているかもしれませんが、とりあえず現時点での解を示したいと思います。

なお、どう書く.orgにもタイムリーなお題が出ていたので、投稿しておきました。
http://ja.doukaku.org/242/

まずは外部コマンドの呼び出しです。
*.disをモジュールとしてロードしinitを呼ぶ、というのがよくある方法のようです。

hdl := Command "/dis/ls.dis"; # ls の例

その後、

hdl->init(ctx, nil); # ctxはメイン関数の第一引数を渡せばよい、第二引数はコマンドの引数リスト

としてやればコマンドが実行される。

spawn hdl->init(ctx, args);

といった感じで別プロセスでの実行も出来るようだ。

この方法とは別に、Shのsystemメソッドを使う方法もある。

sh := load Sys Sys->PATH;
sh->system(ctx, "command");

とするだけでよさそう。簡単。

次にどちらの方法でも良いが、立ち上げた外部プロセスとのパイプライン通信を考える。

Sys->pipeメソッドを使うとパイプからオープン済みのファイルデスクリプタが取得できる。
つまり、標準入出力をファイルI/Oに置き換えることが可能。

fds := array[2] of ref Sys->FD;
sys->pipe(fds);

とすると、fdsという配列が得られ、2つのパイプが得られる。
fds[0]に書き込むとfds[1]からそのデータが読まれ、
fds[1]に書き込むとfds[0]からそのデータが読めるようだ。

なので、外部プロセス実行前に標準出力をどちらかのパイプに結び付けてやり、
もう一方のパイプから読み出せば外部プロセスの出力結果が得られる。
それにはSys->dupを使用する。
Sys->dupの第一引数は結びつけるファイルデスクリプタ、第二引数は結びつける標準入出力の番号(0: stdin, 1: stdout, 2: stderr)。

以上をコードにすると、

sys = load Sys Sys->PATH; # sysのロード
c := load Command "/dis/ls.dis"; # 外部コマンドのロード

fds := array[2] of ref Sys->FD;
sys->pipe(fds); # パイプの取得

sys->dup(fds[0].fd, 1); # パイプ0に標準出力を出す

spawn c->init(ctx, nil); # コマンド実行


buf := array[64] of byte;
n := sys->read(fds[1], buf, len buf); # パイプ1から読み込むとコマンドの出力が得られる



上記コードはエラー処理がまったく入っていないのでよろしくない。

0 件のコメント:

コメントを投稿