l'essentiel est invisible pour les yeux

Sunday, September 03, 2006

Rubyのexpect.rbの使い方

SSHでつないで何か操作を行ったり、パスワードの入力を自動化したり、対話形式のプログラムをプログラム中から利用する場合は、expectモジュールを使用するのが一般的です。

Rubyは標準ライブラリ中にexpect.rbが存在していますが、IO#exceptとしてIOのメソッドとして定義されていて、このままでは扱いにくいので、仮想端末を扱うPTYライブラリとあわせて使用します。


require 'pty'
require 'expect'

module Expect
def spawn(cmd)
  puts "CMD: #{cmd}" if $expect_verbose
PTY.spawn(cmd) do |r,w,pid|
@input_stream = r
@output_stream = w
@child_pid = pid
PTY.protect_signal do
yield
end
end
end

def expect(pat, timeout=10)
ret = @input_stream.expect(pat, timeout) do |match|
raise "expect %s timeout " % (pat.kind_of?(Regexp)? pat.source : pat) unless match
put_cmd(yield(match))
end
end

private
def put_cmd(cmd)
@output_stream.puts(cmd)
end
end
PTY#protect_signalでブロックを実行しないと、SSHで接続後、exitしたときなど子プロセス終了時に例外が発生します。

例えば、rootにログインしてホームディレクトリに移動して、lsコマンドを実行する。

def root_ls
include Expect
spawn("su root") do
expect(/\$$/) {|match| "su root"}
expect(/Password:/) {|match| "myrootpass"}
expect(/\$$/) {|match| "cd"}
expect(/\$$/) {|match| "ls"}
end
end