perl の Expect を使ってみた。でも、動作ログが取れません。マニュアルのリファレンス部分には
1. ファイル名を直接渡す方法」
3. 関数を呼ぶ方法
4. 出力を解除する方法
が載っているが、
2. ファイルハンドルを使う方法
だけ書かれていない。試しに書いてみると
ソースを見ると、ref($file) が 'CODE' だとファイルハンドルと認識するようだが、私のファイルハンドルは GLOB となっている。
う~ん、CODE って何? GLOB って何?(「型グロブ」の「グロブ」?型グロブって何?) という状態で数時間固まっております。
さて、ファイル名を与えると正しく動くことは確認済みなんだが、どうするかなぁ。ファイルハンドルを持ち回した方が使いやすそうなのだが。
誰か教えて~。昔入っていた perl な ML が分からなくなってしまいました。
$object->log_file("filename" | $filehandle | \&coderef | undef)
と書かれていて、ファイルハンドルを使えるように読める。だが、例の部分には1. ファイル名を直接渡す方法」
3. 関数を呼ぶ方法
4. 出力を解除する方法
が載っているが、
2. ファイルハンドルを使う方法
だけ書かれていない。試しに書いてみると
#! /usr/bin/perl -Tw
use lib qw(...略...);
require 5.6.0;
use Expect;
use strict;
$ENV{PATH} = "/bin:/usr/bin";
# log ファイルを開く
open($main::LOG, '>', "log_log");
printf "ref \$main::LOG is %s\n", ref $main::LOG;
my $exp = Expect->spawn("ls", "-l") or die "Cannot spawn ls: $!\n";
# $exp->debug(3);
# $exp->exp_internal(1);
$exp->expect(3 , # timeout
[
qr/.*/s => sub {
$exp->log_file($main::LOG);
$exp->print_log_file("--- ls command. ---");
$exp->send("ls\n");
exp_continue;
}
],
[ timeout => sub { print "timeout?\n"; } ],
);
これを実行use lib qw(...略...);
require 5.6.0;
use Expect;
use strict;
$ENV{PATH} = "/bin:/usr/bin";
# log ファイルを開く
open($main::LOG, '>', "log_log");
printf "ref \$main::LOG is %s\n", ref $main::LOG;
my $exp = Expect->spawn("ls", "-l") or die "Cannot spawn ls: $!\n";
# $exp->debug(3);
# $exp->exp_internal(1);
$exp->expect(3 , # timeout
[
qr/.*/s => sub {
$exp->log_file($main::LOG);
$exp->print_log_file("--- ls command. ---");
$exp->send("ls\n");
exp_continue;
}
],
[ timeout => sub { print "timeout?\n"; } ],
);
> ./test
ref $main::LOG is GLOB
Given logfile doesn't have a 'print' method at ./test line 22
Exit 255
駄目じゃん。ref $main::LOG is GLOB
Given logfile doesn't have a 'print' method at ./test line 22
Exit 255
ソースを見ると、ref($file) が 'CODE' だとファイルハンドルと認識するようだが、私のファイルハンドルは GLOB となっている。
う~ん、CODE って何? GLOB って何?(「型グロブ」の「グロブ」?型グロブって何?) という状態で数時間固まっております。
さて、ファイル名を与えると正しく動くことは確認済みなんだが、どうするかなぁ。ファイルハンドルを持ち回した方が使いやすそうなのだが。
誰か教えて~。昔入っていた perl な ML が分からなくなってしまいました。
そしたら、シェル(bashとか)をspawnしたらどうでしょう?以下のような感じで。
------------
#! /usr/bin/perl -w
use Expect;
use strict;
$ENV{PATH} = "/bin:/usr/bin";
# logファイル名
my $log = "log_log";
# bashをspawnす
my $expect = Expect->spawn("/bin/bash")
or die "Error: Couldn't start program: $!\n";
# STDOUTをオフ
$expect->log_stdout(0);
# logファイの指定
$expect->log_file($log);
# log書き出し
$expect->print_log_file("--- ls command. ---");
# ls -laを打ってみ
$expect->send("ls -la\r");
# タイムアウト1秒で、promptが戻のを捕らえる
$expect->expect(1,qq/.+\$ / );
# exitでbashを閉じ
$expect->send("exit\r");
$expect->soft_close();
$expect->hard_close();
------------
実際には ssh で 3段ぐらい入って、各サーバーで xxx コマンドの結果を集める。(df とか etc etc...)
みたいなことがやりたいので。
$expect->log_stdout(0);
は、確か画面に stdout が出なくなったと記憶していますが、出力先がログファイルに切り替わるのでしょうか?
そうそう、stdout がログファイルに書かれないのも「何でじゃ~」と思ってました。
# ちょっと作業中なので、後で確認します。
$expect->log_stdout(0);
としたのは、単にlogに書き出す指定が
あったので、プロンプトに出さなくても
いいかと思ったので、そうしました。
STDOUTに出したいときは、
$expect->log_stdout(1);
ですね。
sshで入って・・・とのことなので、
spawnをsshに指定すれば出来ますよ~
ls コマンドを spawn してから、それに ls コマンドを投げている。
う~ん、バカσ(;_;)
use lib qw(略);
use Expect;
use strict;
use diagnostics;
use warnings;
$ENV{PATH} = "/bin:/usr/bin";
my $LOG = "log_log";
my @option = ("-l", "ossan", "bun");
my $PASSWORD = "menesis";
my $exp = Expect->spawn("ssh", @option) or die "Cannot spawn ls: $!\n";
$exp->log_stdout(0);
$exp->expect(3 , # timeout
[ qq/password:/ => sub {
$exp->send("$PASSWORD\n");
exp_continue;
} ],
[ qr/login.*]>/s => sub {
$exp->log_file($LOG, "a");
$exp->print_log_file("--- Host: xxx ---\n");
$exp->send("df -h ; echo df done.\n");
$exp->print_log_file("-----------------\n\n");
$exp->log_file(undef);
exp_continue;
} ],
[ qq/df done./ => sub {
$exp->send("exit\n");
exp_continue;
} ],
[ eof => sub {
print "Connection close?\n";
} ],
[ timeout => sub { print "timeout?\n"; } ],
);
@@@@@@@@@@@@@
とすると、
> cat log_log
--- Host: xxx ---
-----------------
@@@@@@@@@@@@@
実行はされているのですが、ログが出ませんねぇ。
謎。spawn sh してから、send ssh ... でもやっぱり同じ。う~ん、expect の中でオートパイロットのようなことをするとログが取れないということか。まぁ、今回は必要ないから良いかな。
spawn で起動
expect で待つ
send でコマンドを送る
expect で待つ
send でコマンドを送る
expect で待つ
...
という方式を採用です。
どうもありがとうございました。 > kiyoji さん
なので、
$exp->log_file(undef);
を、df の結果を読み込んだ後の
$exp->send("exit\n");
の前に置くと上手くいきました。
なによりです。
Expectは、中々便利なので、Mechanizeと
並んで無人君を作るときに私も割りと使っています。
実行結果の取得が
$expect->before();
で、出来たりするので、結果の値を
加工するときなんかオススメです。
同業者の方のようで(^^;;;
> $expect->before();
ですか。ちょっと試してみます。
なるほど、部分的にログを取るならこれで良いのでは?ということですね。
で、こいつもやっぱり
$exp->send("df -h ; echo df done.\n");
$exp->after();
と続けて書いても駄目っぽく。
$exp->send("df -h ; echo df done.\n");
$exp->expect($timeout, "foo");
$exp->after();
と、しないといけない模様。でも、一々部分的に open/close するよりは、最初に open して print LOG $exp->after(); が分かりやすいですね。(エコーバックもちょっと少ないし)