前回はTeraTermマクロで対話型コマンドの実行をがんばった。けど扱いづらい。Perl使いたい。というわけで、telnetコマンドをfork/execしてpipeで標準入出力を操作してみた。*1 *2
まず、ファイルハンドルから値を読み込む関数と書き出す関数。読み出しでは応答が1秒間無かったら入力待ちと判断してる。*3
use strict; use warnings; use IO::Select; sub read_all { my ($stdout_r) = @_; my $read_line; my $s = IO::Select->new($stdout_r); while (1) { my @ready = $s->can_read(1); last if (@ready == 0); sysread($stdout_r, my $in, 1); $read_line .= $in; } return $read_line; } sub writeln { my ($stdin_w, $write_line) = @_; syswrite($stdin_w, "$write_line\n", length("$write_line\n")); }
次がtelnetをfork/execするtelnet関数。ログイン処理と終了処理も行う。*4 *5
sub telnet { my ($user, $pass, $sub) = @_; pipe(my ($stdin_r, $stdin_w )); pipe(my ($stdout_r, $stdout_w)); if (my $pid = fork()) { # parent close($stdin_r); close($stdout_w); print read_all($stdout_r); writeln($stdin_w, $user); print read_all($stdout_r); writeln($stdin_w, $pass); print read_all($stdout_r); $sub->($stdin_w, $stdout_r); writeln($stdin_w, "exit"); print "\n"; kill 9, $pid; close($stdin_w); close($stdout_r); wait(); } elsif (defined $pid) { #child close($stdin_w); close($stdout_r); close(STDIN); close(STDOUT); open STDIN, '<&', $stdin_r; open STDOUT, '>&', $stdout_w; close($stdin_r); close($stdout_w); exec("telnet localhost"); exit 1; } else { die 'fork err'; } }
最後にtelnet関数を使ってlsをたたく。
$| = 1; telnet "hoge", "hoge", sub { my ($stdin_w, $stdout_r) = @_; writeln($stdin_w, "ls"); print read_all($stdout_r); }
車輪の再発明。微妙。。