In this short guide I will show you some tips on how to get started with the *expect* scripting language.
Published: 2021-06-10
Last updated: 2021-06-10 23:40
The expect scripting language is an extension to tcl that can automate applications that have user input prompts. If you haven’t already, then read likegeeks article about it, which is quite comprehensive for starters.
Let’s say we have an application that we know requires specific keyboard input, like filling in forms or pressing Continue at prompts.
We can then write an expect script that waits for a printout by the application that matches specific patterns and then react to them.
#!/usr/bin/env bash
read -p "What is your name? " name
echo -en "Hello, $name!"
The following script can automate the input for the program:
#!/usr/bin/expect -f
# Start the application to automate
spawn ./greet.sh
# Wait for the application to print the prompt
expect {^What is your name\?} {
# If there is a match, respond to it
send -- "Erasmus\r"
}
And is run using the following command:
$ ./tell_name.exp
spawn ./greet.sh
What is your name?
name | function |
---|---|
spawn <program> [[arg0] arg1 ...] |
Start a program in the background. The output of this process can be expect -ed in the script. |
expect pattern [command] [pattern [command] ...] |
Wait for program output that matches pattern , and then execute the command s. |
set timeout <seconds> |
Number of seconds of timeout to use before issuing a timeout condition and continuing to the next command. |
Regular tcl can be used as normal for passing arguments, using conditionals, printing etc.
expect eof
Expect stops printing the output of the program when there are no more expect
commands left. This makes the printouts a bit non-intuitive. The solution is to just add
expect eof
after the last interaction with the program.
The global variable timeout
can be used for setting a timeout for expect commands.
# Timeout as 1 second
set timeout 1
# No timeout
set timeout -1
The following commands will time out if it is configured, and the script will continue.
expect {pattern} {command}
# If pattern never matches within the timeout, this line will be executed next
However, one needs to check for a timeout by using the special timeout pattern:
expect {pattern} {
command
} timeout {
# There was no match against *pattern* within the timeout
puts "ERROR: No match"
exit -1
}
Let’s make a version of the program that takes a bit more time before actually printing anything. So, I added a sleep of 2 seconds:
#!/usr/bin/env bash
sleep 2
read -p "What is your name? " name
echo -en "Hello, $name!"
The expect script is updated with three things:
expect
-ing eof
.#!/usr/bin/expect -f
# Take the timeout as input argument
if { $argc > 0 } {
set timeout [lindex $argv 0]
}
# Start the application to automate
spawn ./greet_slow.sh
# Wait for the application to print the prompt
expect {^What is your name\?} {
# If there is a match, respond to it
send -- "Erasmus\r"
} timeout {
puts "Never found the input prompt during the $timeout second timeout!"
exit -1
}
# Make sure that the output from the program is printed
expect eof
If we run the program with a timeout that is too short (shorter than the actual time before the input prompt appears) then the script times out:
$ ./tell_name_complete.exp 1
spawn ./greet_slow.sh
Never found the input prompt during the 1 second timeout!
However, if we run the program with a longer timeout, then we find the input prompt, and print the rest of the program output after the last prompt.
$ ./tell_name_complete.exp 3
spawn ./greet_slow.sh
What is your name? Erasmus
Hello, Erasmus!