[stdlib] Automation /w Expect

Using Expect & PTY for automating interaction with other processes

1) Simple example, here we are simply spawning a process (an ssh session to my gateway router, Jelly Bean) and after we execute some simple commands over the ssh channel. There is no Exception handling, further process of command output or conditional logic based on return values. However this does give a good introduction to how it works.

#!/usr/bin/env ruby
require 'pty'
require 'expect'

PTY.spawn("ssh -lroot jellybean.lan") do |ssh_out, ssh_in, pid|
    #$expect_verbose = true
    ssh_out.expect(/root@jellybean:~#/) {|read| ssh_in.printf("uptime\r\n") }
    puts ssh_out.expect(/root@jellybean:~#/)[0], "\n"
    ssh_out.expect(/root@jellybean:~#/) {|read| ssh_in.printf("date\r\n") }
    puts ssh_out.expect(/root@jellybean:~#/)[0], "\n"
    ssh_out.expect(/root@jellybean:~#/) {|read| ssh_in.printf("ip addr sh\r\n") }
    puts ssh_out.expect(/root@jellybean:~#/)[0], "\n"
    ssh_out.expect(/root@jellybean:~#/) {|read| ssh_in.printf("ip link sh\r\n") }
    puts ssh_out.expect(/root@jellybean:~#/)[0], "\n"


Lets dig a little deeper into actually using expect for interacting with something, here we are simply interacting with the shell we are running the script on. However we can use this library to interact with other applications. For instance, take this VERY simple bash script. 

#!/bin/bash - 
echo "what do we want?"
read var
if [ "${var}" != "ice cream" ];
    echo "You really want "${var}"?,, Meh, I wanted Ice Cream"
    echo "Haha I wanted some ice cream too!"

Under operation of this script there is input and output, though very basic in nature. Below I have given a couple of runs of the script as it only handles a couple of cases for this test. Input will be denoted by a Blue background and output will be denoted as a Yellow background.

rainofkayos@animal /tmp [4076] % ./input_test.sh
what do we want?
You really want help?,, Meh, I wanted Ice Cream

rainofkayos@animal /tmp [4075] % ./input_test.sh 
what do we want?
ice cream
Haha I wanted some ice cream too!

Given these two outputs, we will now create a script to run this input_test.sh and answer it's question. To make things simpler I will make the answer we want to pass in an argument in the ruby script so we don't have to duplicate functions to test the script out in full.

When we write programs in expect, it kind of indicates we know the program we want to automate pretty well. For instance, I know that the bash script input_test.sh will ask one question "what do we want", and I know it will read in a variable which can be any string completed by a hitting ENTER. I also know that, unless you spell ice cream and hit enter, you will always receive the same string. With that said, here is the Ruby code.

#!/usr/bin/env ruby
require 'pty'
require 'expect'

PTY.spawn("/tmp/input_test.sh") do |it_out, it_in, pid|
    $expect_verbose = true # setting expect to verbose mode.
    it_out.expect(/.*?what do we want\?.*?/) { |read| it_in.printf("#{ARGV[0]}\r\n") }
    puts it_out.expect(/.*?what do we want\?.*?/)

Here is that code in action, running our simple bash script. 

rainofkayos@animal /tmp [4223] % ./run_input_test.rb 
what do we want?

You really want ?,, Meh, I wanted Ice Cream