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
2
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 --
2
3 set low 1
4 set high 101
5 set guess 51
6
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.
The shebang says that this script is interpreted by /usr/bin/expect. This is an Expect script. It's not a shell script.
We use spawn to launch the game as a child process and set up all the magic for driving it.
We use expect for the main body of the driving script. The expect command doesn't automatically loop; we have to use exp_continue to make it keep iterating.
- There are 4 possible responses that we handle. Three of them are informational, and we respond to them by either exiting (because the game is over), or by adjusting our variables.
- The fourth response is a prompt. When we see this, we send our guess, followed by a carriage return (which is what the Enter key sends).
If we set high 100 at the start of the program, we can't handle the number 100. Setting high to 101 allows us to guess the number 100.
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.