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.