OsGate.org Logo

Getting started with Expect - expect script interactivity shell

Script and coding Script and coding

Date 16.01.2012

Visits 6750

"When we are writing a script, we can come to a recurrent problem: the insertion of parameters such as usernames or passwords. In order to resolve this kind of annoying problem, we can use Expect. With its help we can add the missing interactivity touch to our scripts and continue the coding phase."

A Short introduction

Expect is a very useful tool, mainly used for automation purposes. It’s based on Tcl and is one of the best solution if you want to resolve interactivity problems inside your scripts.

In fact, with it is possible to interact with those applications that require a login phase or simply that wait for an input.

For example when you wrote a script that uses ssh, it’s possible that you want to execute it without any kind of prompt, because you have forked the script in the background. In this case Expect can help you by prompting the password for you so your script can happily live in the background.

Expect is developed by Don Libes and the last version is the 5.45 (2010-11-09). It’s released under Public Domain.

Basic instruction

Spawn

This is usually the first instruction that we can find in an Expect script. Thanks to it, it’s possible to create a new process according to the argument passed.

With the name “process” we mean the program that requires the interactivity, such as ssh or ftp.

For example if we want that our script interacts with ftp, this is what we must write:

spawn ftp ftp.foo.bar

This will create a new ftp process, that will connect to ftp.foo.bar. Note that spawn does not modify the normal behavior of the executed program.

Send and Expect

These two instructions are really important, because they are an essential part of Expect.

Expect and Send are the two “main gateway” to the interactivity between the the program launched with spawn and the script.

Expect

This instruction (as the name says) expects an argument. This argument is the output of the previously invoken program (ssh, ftp, …).

Lets make an example:

# ftp ftp.foo.bar
After the connection is made we will have this situation:
--Welcome to an ftp server around the world –



Name (ftp.foo.bar:user):

The prompt is waiting for the username and we can see the “Name (ftp.foo.bar:user):” string.

This is the string that we are expecting in order to give our username.

So we can write an instruction like this:

expect “Name (ftp.foo.bar:user):”

Note that the instruction “expect” can match part of the string, so it’s also possible to write, for example:

expect “Name”

expect “ftp.foo.bar

One of the difference between the first example and the others two, is that with the first, you are really sure that this is the line where you are asked for the username. With the others examples, you cannot be really sure.

If we are expecting a string that doesn’t exist, expect we will jump to the next instruction.

Send

This is the opposite of “expect”. It sends a string to the program we have previously spawned.

If we take the ftp example we have:

# ftp ftp.foo.bar
--Welcome to balbla --



Name (ftp.foo.bar:user):

Now we want to send the username “sysadmin” so that the ftp program can continue with the connection.

send “sysadmin
”

You can notice the presence of “ ” that simulates the enter key. Without it, the script will be wait at this line.

Normally, send is used after expect.

If you want to execute “send” only if the “expect” instruction success, you must use the braces.

expect “username” { send “foo
”}
expect “password” { send “bar ” }

These two send are executed only if the relatives “expect” matched, so if the checks of “username” or “password” fail neither “foo ” or “bar ” are sent.

Interact

Interact is used to give the control of the current process to the user.

This is done in order to obtain the correct output (stdout and stderr) from the executed program and to send keystrokes.

This is an example without “interact”:

#!/usr/bin/expect -f
spawn ssh 192.168.1.98
expect "foo@192.168.1.98's password:"
send "passsh "
expect "foo@bar:~$"
send "free "
expect "total used free shared buffers cached"
send “ls ”
send "exit "

This is what the output looks like:

bash-4.1$ ./ssh.expect
spawn ssh 192.168.1.98
foo@192.168.1.98's password:
Last login: Sun May 1 12:39:01 2010 from 192.168.1.33
Linux 2.6.31.
foo@bar:~$ free
total used free shared buffers cached
Mem: 1552760 1450076 102684 0 72916 1046304
-/+ buffers/cache: 330856 1221904
Swap: 3020180 22628 2997552
foo@bar:~$ bash-4.1$

As we can see, the result is not the same as the expected one, in fact “ls” and “exit” aren’t executed.

 

With “interact” at the end the output is correct:

bash-4.1$ ./ssh.expect
spawn ssh 192.168.1.98
foo@192.168.1.1's password:
Last login: Sun May 1 12:47:51 2010 from 192.168.1.33
Linux 2.6.31.
foo@bar:~$ free
total used free shared buffers cached
Mem: 1552760 1471536 81224 0 74612 1065428
-/+ buffers/cache: 331496 1221264
Swap: 3020180 22628 2997552
foo@bar:~$: ls
Delphi 2A.zip
Delphi 3A.zip
foo@bar:~$: exit
logout
Connection to 192.168.1.98 closed.
bash-4.1$

Another example:

#!/usr/bin/expect -f
spawn ssh 192.168.1.98
expect "foo@192.168.1.98's password:"
send "passsh "
expect "foo@bar:~$"

interact

We have written “interact” after the login phase in order to regain the control over the ssh shell.

TCL

Beign based on TCL, expect is able to interpret all the instructions of it. This can be very useful, because we can extend our script with different functions, like loops or conditional statements.

Lets see some basic expressions:

  • Assign “value” to “variable_name”
set variable_name value
  • Print “string” and add automatically a new line character at the end of the string.
puts string
  • if, elseif, else
if {$var == 1} {
    puts "var 1 ok"
} elseif {$var == 2} {
    puts "var 2 ok"
} else {
    puts "var isn't ok"
}
  • for
for {set i 0} {$iputs "i == $i"
}
  • open a file
 set handle [open file.txt]
  • save the content of a file into a variable
set content [read $handle]

If you want to know more about TCL instructions, read the manual at http://www.tcl.tk/man/tcl8.5/TclCmd/contents.htm.

Examples

SSH

#!/usr/bin/expect -f
set timeout 1
spawn ssh 192.168.1.10
expect -exact "foo@192.168.1.98's password:" {send "bar"}
expect "foo@bar:~$"

send "exit"
interact

With “set timeout 1” expect will wait 1 seconds before execute the next instruction. This can be useful to avoid output overlapping.

The option “-exact” specifies that the expected string must be exactly equal to the specified one.

FTP

#!/usr/bin/expect -f
set timeout 1
spawn ftp ftp.foo.bar
expect "Name (ftp.foo.bar:foo):" {send "admin "}

expect "Password:" {send "ftpuser"}
expect "ftp>"
send "exit"
interact