SSHでつないで何か操作を行ったり、パスワードの入力を自動化したり、対話形式のプログラムをプログラム中から利用する場合は、expectモジュールを使用するのが一般的です。
Rubyは標準ライブラリ中にexpect.rbが存在していますが、IO#exceptとしてIOのメソッドとして定義されていて、このままでは扱いにくいので、仮想端末を扱うPTYライブラリとあわせて使用します。
PTY#protect_signalでブロックを実行しないと、SSHで接続後、exitしたときなど子プロセス終了時に例外が発生します。
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
例えば、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