1. Overview

The expect tool and Expect scripts, which use expect as the interpreter, are useful for automating and controlling interactive applications.

expect supports several commands. For example, it’s possible to spawn a process using the spawn command.

In this tutorial, we’ll discuss how to get the exit code of a spawned process within an Expect script.

2. Expect Scripts

Some applications such as passwd, ssh, and telnet need interaction with the user. Therefore, it’s difficult to automate this type of interactive programs in shell scripts.

expect is a tool for automating and controlling interaction with interactive applications like passwd. We use expect as the interpreter in Expect scripts, i.e., we parse Expect scripts using expect.

expect is an extension of Tcl (Tool Command Language), so we can use the constructs of the Tcl language in Expect scripts. However, expect also provides additional commands.

The most basic commands of expect are spawn, send, and expect. spawn creates a new process. send sends a string to the spawned process. expect, on the other hand, waits for a pattern in the output of the spawned process.

Let’s consider the usage of the passwd command to understand the usage of these basic expect commands. When we run the passwd command, it wants us to enter the current password:

$ passwd
Changing password for alice.
Current password:

We can automate this part for the user alice using the following commands in an Expect script:

spawn passwd
expect "Changing password for alice."
expect "Current password: "
send --  "old_password\r"

spawn passwd command runs the passwd command.

We expect passwd to print the statements “Changing password for alice.” and “Current password: “. So, we check the existence of these statements in the output of passwd using the expect “Changing password for alice.” and expect “Current password: “ commands. Otherwise, the script fails.

Finally, we provide the current password expected by passwd using the send —  “old_password\r” command. The \r character, carriage return, at the end of the password corresponds to pressing Enter. The flag after send forces the next argument to be interpreted as a string rather than a flag.

Having seen some brief information about Expect scripts, let’s discuss how to get the exit code of a process spawned in an Except script in the following section.

3. Getting the Exit Code of a Process

As an example, we’ll spawn a process by running a Bash script from an Expect script, and we’ll get the exit code of the process within the Expect script.

3.1. The Bash Script

We’ll call the following Bash script, exit_code.sh, from the Expect script:

!/bin/bash

exit $1

This Bash script just exits with the exit code provided to it as a parameter. For example, if we pass 30 as the argument to it, the exit code of the script is 30:

$ ./exit_code.sh 30
$ echo $?
30

3.2. The Expect Script

The Expect script we’ll use is get_exit_code.exp:

#!/usr/bin/expect

set exit_code [lindex $argv 0]

spawn ./exit_code.sh $exit_code

lassign [wait] pid spawn_id os_error actual_exit_code

if {$os_error == 0} {
    puts "exit code: $actual_exit_code"
} else {
    puts "errno: $actual_exit_code"
}

Expect scripts generally have the .exp extension to differentiate them from other scripts, but this extension isn’t mandatory.

3.3. Understanding the Expect Script

Let’s understand get_exit_code.exp line by line.

This first line is the shebang for the Expect script:

#!/usr/bin/expect

/usr/bin/expect is the interpreter for the Expect script, i.e., the shebang tells the operating system to use /usr/bin/expect to parse the rest of the file.

Next, we get the exit code we’ll pass to exit_code.sh from the command line:

set exit_code [lindex $argv 0]

This expression gets the first argument of the script using lindex argv 0. The Tcl command, lindex, retrieves an element from a list. argv is a global Tcl list holding the parameters passed to the script. The index 0 corresponds to the first element in argv.

Then, we assign [lindex $argv 0] to the variable exit_code using the Tcl set command. Brackets correspond to command substitution in Tcl.

Next, we execute the Bash script using spawn:

spawn ./exit_code.sh $exit_code

Then, we get the spawned process’ exit code using the wait command of expect:

lassign [wait] pid spawn_id os_error actual_exit_code

If there aren’t any parameters passed to wait, wait delays until the spawned process terminates.

wait returns a list of four integers when the spawned process terminates.

The first integer is the PID of the process. The second one corresponds to the spawn ID, which is a descriptor referring to the process.

The third integer is -1 if an operating system error occurs while running the program. Otherwise, it’s 0.

Finally, if the third integer is 0, the last integer in the list is the exit code of the process. However, if the third integer is -1, the last integer is the error number the operating system sets.

The Tcl lassign command assigns list elements to variables. So, lassign assigns the four integers wait returns to the pid, spawn_id, os_error and actual_exit_code variables.

Finally, we print the exit code:

if {$os_error == 0} {
    puts "exit code: $actual_exit_code"
} else {
    puts "errno: $actual_exit_code"
}

The if part of this code snippet prints the exit code using the puts command. The Tcl puts command writes the output to a channel, but if no channel is specified, it writes to stdout.

However, if an error occurs while executing the process, then we print the error number in the else part.

3.4. Running the Expect Script

Now, let’s try the Expect script get_exit_code.exp:

$ ./get_exit_code.exp 0
spawn ./exit_code.sh 0
exit code: 0
$ ./get_exit_code.exp 111
spawn ./exit_code.sh 111
exit code: 111

We executed the script twice by supplying the arguments 0 and 111 to it. The exit codes of the processes spawned by running ./exit_code.sh 0 and ./exit_code.sh 111 are 0 and 111, as expected.

4. Conclusion

In this article, we discussed how to get the exit code of a spawned process within an Expect script.

First, we learned that the expect tool is useful for automating and controlling interactive applications like passwd. Additionally, we learned that we can use Tcl commands in Expect scripts as expect is an extension of Tcl.

Then, we discussed an example Expect script in which we spawn a process by running a Bash script. We saw that we could use the wait command of expect to get the exit code of the spawned process.