Expect example

Many people come to the #bash IRC channel asking about Expect. Expect is a package written in and for Tcl, so it's not supported by the #bash channel. Moreover, some people seem to think that they should call expect as a command from their bash scripts. This is not how it's best used.

Expect is a Tcl package, and also a standalone tool incorporating a Tcl interpreter. If you use it, you will be writing a Tcl program, not a bash script.

That said, it doesn't hurt to show an example of how to use it. There seems to be a demand for it. So, let's go for it.

The program we're driving

Expect is used to "drive" an interactive program using a script. The script needs to have some idea of how the interactive program will work, so that it can anticipate certain key pieces of output, and know what input to provide. Therefore, the first thing you should do is actually run the interactive program yourself, and try to find all the possible responses that you'll need to handle.

We're going to drive a simple number-guessing game. Here's the game:

   1 #!/bin/bash
   3 number=$((RANDOM % 100 + 1))
   4 while read -r -p 'Your guess: ' guess; do
   5     # Input validation goes here.  Omitted for this example.
   6     if ((guess == number)); then
   7         echo 'You won!'
   8         break
   9     elif ((guess < number)); then
  10         echo 'Too small, try again.'
  11     else
  12         echo 'Too large, try again.'
  13     fi
  14 done

If you run this in a terminal and interact with it, you'll see that its output consists of either (1) a single line telling you whether you've won, or guessed too high, or guessed too low; or (2) a prompt. When you see the prompt, you're supposed to type a number and press Enter. So, that's what our Expect script will need to do.

The Expect script

In addition to handling the basic responses from the game, we need some sort of strategy for how to actually play it. The naive way would be to guess every number from 1 to 100 until we win, and that would work. But the optimal solution is of course to use a binary search. This means we need to keep some variables that track the lower and upper limits of the current possible range. Each time we guess, we choose the number in the middle of the range. If we guess too low, then we move the lower bound up to our guess. If we guess too high, we move the upper bound down to our guess.

Here's the Expect script. It's written for a system where expect is installed as a standalone tool in /usr/bin/expect. If your system has it in a different place, you can change the shebang. If you only have it installed as a Tcl package, then you may need to use a tclsh shebang, and load the package.

   1 #!/usr/bin/expect --
   3 set low 1
   4 set high 101
   5 set guess 51
   7 spawn ./game
   8 expect {
   9     {You won} exit
  10     {Too small} {
  11         set low $guess
  12         exp_continue
  13     }
  14     {Too large} {
  15         set high $guess
  16         exp_continue
  17     }
  18     {Your guess:} {
  19         set guess [expr {($low + $high) / 2}]
  20         send "$guess\r"
  21         exp_continue
  22     }
  23 }

What do you need to know about this program? There are only a few important pieces.

And there you have it. Now you know how to drive a number-guessing game using Expect. You can apply these same techniques to drive many other interactive programs.


ExpectExample (last edited 2021-08-13 01:53:39 by GreyCat)