While attempting to automate my workflow, I stumble on a configuration utility that defied significant automation. It was a Java course of that did not help a silent installer, or help stdin
, and had an inconsistent set of prompts. Ansible’s count on
module was insufficient for this process. But I discovered that the count on
command was simply the instrument for the job.
My journey to study Expect meant learning a bit of Tcl. Now that I’ve the background to create easy applications, I can higher study to program in Expect. I believed it will be enjoyable to jot down an article that demonstrates the cool performance of this venerable utility.
This article goes past the everyday easy recreation format. I plan to make use of components of Expect to create the sport itself. Then I reveal the actual energy of Expect with a separate script to automate enjoying the sport.
This programming train reveals a number of basic programming examples of variables, enter, output, conditional analysis, and loops.
Install Expect
For Linux primarily based methods use:
$ sudo dnf set up count on
$ which count on
/bin/count on
I discovered that my model of Expect was included within the base working system of macOS:
$ which count on
/usr/bin/count on
On macOS, you may also load a barely newer model utilizing brew:
$ brew set up count on
$ which count on
/usr/native/bin/count on
Guess the quantity in Expect
The quantity guessing recreation utilizing Expect will not be that completely different from the bottom Tcl I utilized in my previous article.
All issues in Tcl are strings, together with variable values. Code strains are finest contained by curly braces (Instead of attempting to make use of line continuation). Square brackets are used for command substitution. Command substitution is helpful for deriving values from different capabilities. It can be utilized straight as enter the place wanted. You can see all of this within the subsequent script.
Create a brand new recreation file numgame.exp
, set it to be executable, after which enter the script beneath:
#!/usr/bin/count on
proc used_time {begin} {
return [expr [clock seconds] - $begin]
}
set num [expr round(rand()*100)]
set starttime [clock seconds]
set guess -1
set rely 0
ship "Guess a number between 1 and 100n"
whereas { $guess != $num } {
incr rely
ship "==> "
count on {
-re "^([0-9]+)n" {
ship "Read in: $expect_out(1,string)n"
set guess $expect_out(1,string)
}
-re "^(.*)n" {
ship "Invalid entry: $expect_out(1,string) "
}
}
if { $guess < $num } {
ship "Too small, try againn"
} elseif { $guess > $num } {
ship "Too large, try againn"
} else {
ship "That's right!n"
}
}
set used [used_time $starttime]
ship "You guessed value $num after $count tries and $used elapsed secondsn"
Using proc
units up a perform (or process) definition. This consists of the identify of the perform, adopted by an inventory containing the parameters (1 parameter {begin}
) after which adopted by the perform physique. The return assertion reveals an excellent instance of nested Tcl command substitution. The set
statements outline variables. The first two use command substitution to retailer a random quantity and the present system time in seconds.
The whereas
loop and if-elseif-else logic ought to be acquainted. Note once more the actual placement of the curly braces to assist group a number of command strings collectively without having line continuation.
The large distinction you see right here (from the earlier Tcl program) is using the capabilities count on
and ship
relatively than utilizing places
and will get
. Using count on
and ship
type the core of Expect program automation. In this case, you employ these capabilities to automate a human at a terminal. Later you may automate an actual program. Using the ship
command on this context is not way more than printing info to display screen. The count on
command is a little more advanced.
The count on
command can take a couple of completely different varieties relying on the complexity of your processing wants. The typical use consists of certainly one of extra pattern-action pairs comparable to:
count on "pattern1" {action1} "pattern2" {action2}
More advanced wants can place a number of sample motion pairs inside curly braces optionally prefixed with choices that alter the processing logic. The type I used above encapsulates a number of pattern-action pairs. It makes use of the choice -re
to use regex processing (as an alternative of glob processing) to the sample. It follows this with curly braces encapsulating a number of statements to execute. I’ve outlined two patterns above. The first is Is meant to match a string of 1 or extra numbers:
"^([0-9]+)n"
The second sample is designed to match the rest that isn’t a string of numbers:
"^(.*)n"
Take word that this use of count on
is executed repeatedly from inside a whereas
assertion. This is a wonderfully legitimate method to studying a number of entries. In the automation, I present a slight variation of Expect that does the iteration for you.
Finally, the $expect_out
variable is an array utilized by count on
to carry the outcomes of its processing. In this case, the variable $expect_out(1,string)
holds the primary captured sample of the regex.
Run the sport
There ought to be no surprises right here:
$ ./numgame.exp
Guess a quantity between 1 and 100
==> Too small, strive once more
==> 100
Read in: 100
Too giant, strive once more
==> 50
Read in: 50
Too small, strive once more
==> 75
Read in: 75
Too small, strive once more
==> 85
Read in: 85
Too giant, strive once more
==> 80
Read in: 80
Too small, strive once more
==> 82
Read in: 82
That's proper!
You guessed worth 82 after 8 tries and 43 elapsed seconds
One distinction it’s possible you’ll discover is the impatience this model reveals. If you hesitate lengthy sufficient, count on timeouts with an invalid entry. It then prompts you once more. This is completely different from will get
which waits indefinitely. The count on
timeout is a configurable function. It helps take care of hung applications or throughout an surprising output.
Automate the sport in Expect
For this instance, the Expect automation script must be in the identical folder as your numgame.exp
script. Create the automate.exp
file, make it executable, open your editor, and enter the next:
#!/usr/bin/count on
spawn ./numgame.exp
set guess [expr round(rand()*100)]
set min 0
set max 100
places "I'm starting to guess using the number $guess"
count on {
-re "==> " {
ship "$guessn"
count on {
"Too small" {
set min $guess
set guess [expr ($max+$min)/2]
}
"Too large" {
set max $guess
set guess [expr ($max+$min)/2]
}
-re "value ([0-9]+) after ([0-9]+) tries and ([0-9]+)" {
set tries $expect_out(2,string)
set secs $expect_out(3,string)
}
}
exp_continue
}
"elapsed seconds"
}
places "I finished your game in about $secs seconds using $tries tries"
The spawn
perform executes this system you wish to automate. It takes the command as separate strings adopted by the arguments to move to it. I set the preliminary quantity to guess, and the actual enjoyable begins. The count on
assertion is significantly extra sophisticated and illustrates the ability of this utility. Note that there isn’t a looping assertion right here to iterate over the prompts. Because my recreation has predictable prompts, I can ask count on
to do some extra processing for me. The outer count on
makes an attempt to match the sport enter immediate of `==>` . Seeing that, it makes use of ship
to guess after which makes use of an extra count on
to determine the outcomes of the guess. Depending on the output, variables are adjusted and calculated to arrange the following guess. When the immediate `==>` is matched, the exp_continue
assertion is invoked. That causes the outer count on
to be re-evaluated. So a loop right here is now not wanted.
This enter processing depends on one other habits of Expect’s processing. Expect buffers the terminal output till it matches a sample. This buffering contains any embedded finish of line and different unprintable characters. This is completely different than the everyday regex line matching you’re used to with Awk and Perl. When a sample is matched, something coming after the match stays within the buffer. It’s made out there for the following match try. I’ve exploited this to cleanly finish the outer count on
assertion:
-re "value ([0-9]+) after ([0-9]+) tries and ([0-9]+)"
You can see that the internal sample matches the right guess and doesn’t devour the entire characters printed by the sport. The final a part of the string (elapsed seconds) continues to be buffered after the profitable guess. On the following analysis of the outer count on
, this string is matched from the buffer to cleanly finish (no motion is equipped). Now for the enjoyable half, let’s run the complete automation:
$ ./automate.exp
spawn ./numgame.exp
I'm beginning to guess with the quantity 99
Guess a quantity between 1 and 100
==> 99
Read in: 99
Too giant, strive once more
==> 49
Read in: 49
Too small, strive once more
==> 74
Read in: 74
Too giant, strive once more
==> 61
Read in: 61
Too small, strive once more
==> 67
Read in: 67
That's proper!
You guessed worth 67 after 5 tries and 0 elapsed seconds
I completed your recreation in about 0 seconds utilizing 5 tries
Wow! My quantity guessing effectivity dramatically elevated because of automation! A number of trial runs resulted in anyplace from 5-8 guesses on common. It additionally all the time accomplished in beneath 1 second. Now that this pesky, time-consuming enjoyable could be dispatched so rapidly, I’ve no excuse to delay different extra essential duties like engaged on my home-improvement initiatives 😛
Never cease studying
This article was a bit prolonged however nicely definitely worth the effort. The quantity guessing recreation supplied an excellent base for demonstrating a extra fascinating instance of Expect processing. I discovered fairly a bit from the train and was in a position to full my work automation efficiently. I hope you discovered this programming instance fascinating and that it lets you additional your automation objectives.