1. Overview

expect is an application that can help us automate bash scripts by answering prompts for us. For example, a bash script may ask us for two inputs interactively. Rather than wait for keyboard input, we might want to automate the process entirely. Using expect, we can automatically send our inputs.

By default, expect displays the interaction with the script. However, if expect is used to enter secret information, we may wish to hide it from the console.

In this tutorial, we’ll see how to use different methods to hide the output of expect.

2. Output from expect

We need to make sure we have expect installed. In Ubuntu, we install it with apt:

$ sudo apt install expect -y

In Fedora, we can install expect with dnf:

$ sudo dnf -y install expect

2.1. Example Script

Let’s create a Bash script that prompts us for input, which we’ll automate later.

We should name the script file agent_placement.sh:

#!/bin/bash
echo "What is your name, agent?"
read NAME
echo "Where do you want to be assigned to?"
read PLACE
echo "Alright, $NAME! You'll be assigned to $PLACE."
echo "<SECRET>Be careful, $PLACE is a dangerous place.</SECRET>"

This bash script accepts two inputs interactively. Let’s execute the Bash script, after first setting it to executable with chmod:

$ chmod +x agent_placement.sh
$ ./agent_placement.sh
What is your name, agent?
<waiting for input>James Bond
Where do you want to be assigned to?
<waiting for input>Berlin
Alright, James Bond! You'll be assigned to Berlin.
<SECRET>Be careful, Berlin is a dangerous place.</SECRET>

The bash script paused for input twice.

2.2. Output Displayed When Executing expect

Now, let’s execute the script but with expect.

First, we need to write an expect script. Let’s create a file named expect_agent_placement.sh:

#!/usr/bin/expect
spawn ./agent_placement.sh
expect -exact "What is your name, agent?\r"
send -- "James Bond\r"
expect -exact "Where do you want to be assigned to?\r"
send -- "Paris\r"
expect eof

This time, we don’t have to type our inputs manually when executing the bash script. The expect script takes care of it for us.

After making the script executable, we can run it to see how it interacts with our agent_placement.sh script:

$ chmod +x expect_agent_placement.sh
$ ./expect_agent_placement.sh 
spawn ./agent_placement.sh
What is your name, agent?
James Bond
Where do you want to be assigned to?
Paris
Alright, James Bond! You'll be assigned to Paris.
<SECRET>Be careful, Paris is a dangerous place.</SECRET>

As we can see, the expect script displayed the output. If this input were sensitive, this might be a problem.

3. Hiding Output from expect

We might be able to rewrite our bash script to avoid displaying sensitive data, but sometimes that’s not an option. So let’s look at how to suppress output within the expect script.

3.1. Redirecting stdout to /dev/null

We could redirect the output of the expect script to /dev/null to prevent any output from being shown:

$ ./expect_agent_placement.sh > /dev/null

As we can see, no output is shown. However, this is a very broad approach to solving the problem. We may wish to see some parts of the script’s output rather than nothing.

3.2. Using log_user

We can use the log_user command in our expect script to hide the output that follows.

Let’s create another expect script and name it expect_agent_placement_log_user.sh:

#!/usr/bin/expect
spawn ./agent_placement.sh
expect -exact "What is your name, agent?\r"
log_user 0
send -- "James Bond\r"
expect -exact "Where do you want to be assigned to?\r"
send -- "Paris\r"
expect eof

Here we added the log_user command with the value 0 to hide the output following that line.

Let’s run the script:

$ ./expect_agent_placement_log_user.sh 
spawn ./agent_placement.sh
What is your name, agent?

This time, we get some output, but it stops when the expect script reaches the log_user 0 command.

If, in the middle of the script, we wanted to start outputting again, we could use the log_user command again, with the value 1.

Let’s edit expect_agent_placement_log_user.sh and put log_user 1 above the Paris line:

#!/usr/bin/expect
spawn ./agent_placement.sh
expect -exact "What is your name, agent?\r"
log_user 0
send -- "James Bond\r"
expect -exact "Where do you want to be assigned to?\r"
log_user 1
send -- "Paris\r"
expect eof

Let’s execute the script to see the difference:

$ ./expect_agent_placement_log_user.sh
spawn ./agent_placement.sh
What is your name, agent?
Paris
Alright, James Bond! You'll be assigned to Paris.
<SECRET>Be careful, Paris is a dangerous place.</SECRET>

This time, after hiding the output for a bit, the script resumed output from the middle of the script.

3.3. Using ssty -echo

Suppose we want to type the second input manually but automate the first input with the expect script.

Let’s create a script expect_agent_placement_echo.sh:

#!/usr/bin/expect
send_user "place: "
expect_user -re "(.*)\n"
set place $expect_out(1,string)
spawn ./agent_placement.sh
expect -exact "What is your name, agent?\r"
send -- "James Bond\r"
expect -exact "Where do you want to be assigned to?\r"
send -- "$place\r"
expect eof

This script asks for our place input but automates the name input with the value James Bond. Let’s execute the script to see the difference from the original script:

$ ./expect_agent_placement_echo.sh 
place: <waiting for input>Madrid
spawn ./agent_placement.sh
What is your name, agent?
James Bond
Where do you want to be assigned to?
Madrid
Alright, James Bond! You'll be assigned to Madrid.
<SECRET>Be careful, Madrid is a dangerous place.</SECRET>

We filled Madrid when we’ve been asked for the input. Then the script put our input into a variable. Later, the script sent the variable into the second input of the bash script.

As we can see, when we typed Madrid, the input was displayed. But we might want to hide it for secrecy.

For this, we can use the stty -echo command. Let’s add the command to the script above the send_user line:

#!/usr/bin/expect
stty -echo
send_user "place: "
expect_user -re "(.*)\n"
set place $expect_out(1,string)
spawn ./agent_placement.sh
expect -exact "What is your name, agent?\r"
send -- "James Bond\r"
expect -exact "Where do you want to be assigned to?\r"
send -- "$place\r"
expect eof

Then let’s execute the script so we can see the input is hidden:

$./expect_agent_placement_echo.sh
place: <waiting for input>

When we type Madrid, the input is not shown. After pressing Enter, we will get this output:

$ ./expect_agent_placement_echo.sh 
place: spawn ./agent_placement.sh
What is your name, agent?
James Bond
Where do you want to be assigned to?
Madrid
Alright, James Bond! You'll be assigned to Madrid.
<SECRET>Be careful, Madrid is a dangerous place.</SECRET>

As we can see, our input for the place input was hidden.

4. Conclusion

In this article, we learned how to hide the output of expect.

First, we wrote a bash script and an expect script that automated the bash script. We executed the expect script and saw the output being displayed.

Then we redirected the output of expect to /dev/null to hide the output. But we wanted to hide certain parts of the output, so we used log_user.

Finally, we used stty -echo to hide our input for the expect script.