Ruby Expect

Original from RubyGarden

#!/usr/bin/ruby
#
# $Id$
#
# RExpect.rb
#
 
require 'pty'
require 'thread'
require 'timeout'
 
$expect_verbose = true
 
Thread.abort_on_exception = true
 
class RExpect
  attr_reader :pid
 
  def RExpect.spawn(cmd)
    mutex = Mutex.new
    mutex.synchronize do
 
      resultResource = ConditionVariable.new
      result = nil
      Thread.new do
        pid = nil
        mutex.synchronize do
          inf, outf, pid, fileName = PTY.spawn(cmd)
          result = RExpect.new( inf, outf, pid, fileName)
          resultResource.signal
        end
        result.inputData
      end
 
      resultResource.wait( mutex)
      return result
    end
  end
 
  def log(a)
    @log.puts "#{caller(1)[0]}:#{a}" unless @log.nil?
  end
 
  def initialize(inf,outf,pid,fileName)
    @inf,@outf,@pid,@fileName = inf,outf,pid,fileName
    if $expect_verbose then
      @log = File.open( "rexpect.log.#{@pid}", "w")
      @log.sync = true
    else
      @log = nil
    end
 
    @outf.sync=true
    @buffer = ''
    @mutex = Mutex.new
    @bufferResource = ConditionVariable.new
  end
 
  def wait
    Process.waitpid(@pid,0)
  rescue
  end
 
  def kill
    kill( "SIGTERM", @pid)
    wait
  end
 
  def inputData
    while true
      data = @inf.sysread( 65536)
      @mutex.synchronize do
        log( "BUFFER:#{@buffer}\nNEWDATA:#{data}" )
        @buffer << data
 
        # Alternately: if !@e_pat.nil? && (md = @e_pat.match(@buffer))
        if !@e_pat.nil? && @buffer =~ @e_pat then
          # Alternately (if using the above) @result = [@buffer, *md.to_a[1 .. -1]]
          @result = [@buffer,$1,$2,$3,$4,$5,$6,$7,$8,$9]
          @buffer = $'
          log( "Buffer matched#{@e_pat.source}")
          @e_pat = nil
          @bufferResource.signal
        end
      end
    end
  rescue SystemCallError, EOFError => details
    log( details)
    log( details.backtrace.join("\n"))
    @mutex.synchronize do
      @bufferResource.signal
    end
  rescue RuntimeError => details
    raise details if details.to_s !~ /Child_changed/
  ensure
    @log.close
  end
 
  def expect(pat,time_out=5)
    @result = nil
 
    @mutex.synchronize do
      case pat
      when String
        @e_pat = Regexp.new(Regexp.quote(pat))
      when Regexp
        @e_pat = pat
      end
      log( @e_pat.source)
 
      if @buffer =~ @e_pat then
        @result = [@buffer,$1,$2,$3,$4,$5,$6,$7,$8,$9]
        @buffer = $'
        log( "Existing buffer matched#{@e_pat.source}")
        @e_pat = nil
        return @result
      end
      timeout( time_out) do
        @bufferResource.wait( @mutex)
        raise EOFError if @result.nil?
      end
    end
    return @result
  end
 
  def puts(str)
    log( str)
    @outf.puts str
  end 
 
end
 
 
if (__FILE__ == $0)
  #
  # Here is a silly example....
  #
  $log = File.open( "hello.log", "w")
  $me = RExpect.new( STDIN, STDOUT, Process.ppid, "stdio")
 
  Thread.new do
    # Note this _must_ run in its own thread! 
    # Spawn creates its own thread to do this...
    $me.inputData
  end
 
  def try( pat)
    $log.puts "Seeking:#{pat.source}"
    result = $me.expect( pat, 6)
    $log.puts "Found:#{result}"
    return result
  end
 
  def say( s)
    $log.puts "Say: #{s}"
    $me.puts s
  end
 
  say "Hello world"
  say "Expecting me?"
  try( /You again!/)
  say "Ah well, since you're here, whats your name then?"
  result = try( /[\n\r]([^\n\r]+)/)
  if result.nil? then
    say "Cat got your tongue?"
  else 
    if result[1] =~ /John/ then
      say "Not you again"
    else
      say "Please to meet you"
    end
  end
  try( /Go away!/)
  say "Will do"
  $log.puts "Exiting"
  $log.close
end

Back to Ruby

rubyexpect.txt · Last modified: 2014/10/26 01:52 (external edit)
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0