Automating responses to scripts on Linux using expect and autoexpect

The Linux expect command takes script writing to an entirely new level. Instead of automating processes, it automates running and responding to other scripts. In other words, you can write a script that asks how you are and then create an expect script that both runs it and tells it that you’re ok.

Here’s the bash script:

#!/bin/bash echo "How are you doing?"
read ans

Here’s the expect script that provides the response to the query:

#!/usr/bin/expect set timeout -1
spawn ./ask # ask is name of script to be run
expect "How are you doing?\r"
send -- "ok\r"
expect eof

When you run the script, you should see this:

$ ./ask.exp
spawn ./ask
How are you doing?
ok

Note that you would just invoke the expect script (ask.exp). The script itself answers the question for you.

The first line of the expect script (ask.exp) shown above disables a possible timeout. The second kicks off the script that will be asking for a response using spawn.

Note that the expected prompts have to match what is asked precisely. Even “How are you doing?” and “How are you doing? “ (note the extra space between the question mark and quotation mark) aren’t matches. And, if your script includes the command echo -n “How are you doing? “, the expect script needs to use expect “How are you doing? “, not expect “How are you doing? \n”.

Here’s a slightly more complicated example. If a script that you use asks you to identify a directory that you want to back up in the form of a compressed tar file, it might look like this:

#!/bin/bash echo -n "Directory> "
read dir
tar -czf $dir.tar.gz ~/$dir

The corresponding expect script that runs the script and provides the name of the directory to be backed up might look like this:

#!/usr/bin/expect set timeout -1
spawn ./dirbackup
expect "Directory> "
send -- "PNGs\r"
expect eof

Obviously, this script isn’t particularly useful because it backs up just one particular directory, and you could do that more easily by just running the tar command. For more complex interactions, this would not be the case.

When to use expect

In general, the only time you would likely want to use expect is with scripts that ask way more questions than you feel like answering or that you want to run when you’re not around. For example, a script used to install some particular application might ask six critical questions and then require that you reply “y” many more times before proceeding with the install. There may be times, too, that you might need to install an application on 32 servers or simply want or have it installed overnight while no one is using them. You could automate that process by asking expect to run the script and  provide all of the answers for you.

Here’s an example, though the script in this case isn’t really doing anything with the answers that it collects.

The install script:

#!/bin/bash echo -n "Location of installation file> "
read dir
echo -n "Which components to you wish to install [all,core,enduser]> "
read ans
echo -n "Is your license file up-to-date? [Y,n]> "
read ans
echo -n "Should user responses be logged? [Y,n]> "
read ans
echo -n "Enter Y if ready to begin install [Y,n]> "
read ans

The expect script that might run this script will look similar:

#!/usr/bin/expect set timeout -1
spawn ./installapp expect "Location of installation file> "
send "/var/installs\r"
expect "Which components to you wish to install \\\[all,core,enduser\\\]> "
send "all\r"
expect "Is your license file up-to-date? \\\[Y,n\\\]> "
send "Y\r"
expect "Should user responses be logged? \\\[Y,n\\\]> "
send "Y\r"
expect "Enter Y if ready to begin install \\\[Y,n\\\]> "
send "Y\r"
expect eof

The install script would, of course, need to use the collected answers and run the proper commands to install the required software. The trick here is to capture the questions that will be asked and send the right answers to each one.

Note that the \\\ sequences used in this expect script are required to escape the square brackets.

Autoexpect

There is a way to make preparing expect scripts a lot easier. That is, you can provide your script as an argument to the autoexpect command and it will create the expect script for you. You use a command like autoexpect ./installapp and it will build an expect script with the answers that you provide:

$ autoexpect ./installapp
autoexpect started, file is script.exp
Location of installation file> /var/installs
Which components to you wish to install [all,core,enduser]> all
Is your license file up-to-date? [Y,n]> Y
Should user responses be logged? [Y,n]> Y
Enter Y if ready to begin install [Y,n]> Y
autoexpect done, file is script.exp

The resultant script.exp file will then include an explanation that it was created with autoexpect and will include the responses you provided. Here’s what we’d see in the file after the interactions shown above:

$ tail -18 script.exp
set timeout -1 spawn ./ask04 match_max 100000 expect -exact "Location of installation file> " send -- "/var/installs\r" expect -exact "/var/installs\r Which components to you wish to install \[all,core,enduser\]> " send -- "all\r" expect -exact "all\r Is your license file up-to-date? \[Y,n\]> " send -- "Y\r" expect -exact "Y\r Should user responses be logged? \[Y,n\]> " send -- "Y\r" expect -exact "Y\r Enter Y if ready to begin install \[Y,n\]> " send -- "Y\r" expect eof

The autoexpect tool prepares the script for non-interactively running an installation. You can then do installs without having to supply the details or just schedule them to run on their own.

Wrap-Up

The expect command is handy for running scripts that require a long series of answers and allows you to run them in an un-manned fashion while autoexpect makes it easy to create expect scripts without stressing out over the syntactical details.

Join the Network World communities on Facebook and LinkedIn to comment on topics that are top of mind.