1. Overview

When writing bash scripts, we may want to answer interactive prompts from one of the programs we invoke to automate a process or to make a script run unattended.

In this tutorial, we’ll see how to use different methods to answer interactive prompts in a bash script.

We’ll start with simple scripts that answer yes/no questions like in common command-line applications. Then we’ll move on to smarter scripts that can answer prompts based on custom logic.

2. Using yes to Answer Yes/No Prompts

yes is a simple command-line application that is built-in to many Linux distributions. We can use yes to output the same answer over and over in case there is a prompt, so our script won’t be interrupted waiting for input when a program asks a yes/no question. To use yes, we should pipe its output to the standard input of the program we use it with.

2.1. A Sample Command That Asks a Yes/No Question

Let’s see an example command that prompts for a yes/no answer. If we run:

touch test.txt
rm -i *.txt

We’ll get a yes/no prompt:

/bin/rm: remove regular empty file 'test.txt'?

If we are doing this manually, we can just type y <Enter> and pass this prompt. But if we’re writing an automated script, we wouldn’t want this prompt to stop our script mid-execution.

Normally in this situation, we should prefer using the -f flag of the rm command to disable prompts. But for this article, we’ll go with the rm command to explain how to use yes to answer yes/no prompts, either by replying yes or no to every prompt.

2.2. Using yes

Let’s run the yes command in a terminal:

y
y
y
...

It will just produce the output y repeatedly.

So to automate our script, we can pipe the standard output stream of the yes program to the standard input stream of the command we want to answer the prompt of:

touch test.txt
yes | rm -i *.txt

Therefore, our script will answer y if any prompt is generated by the command and won’t be blocked during its execution.

2.3. Using yes With Arguments

We can also determine what will be the output of the yes command. For example, we can pass a parameter so that it answers n to every prompt:

yes n | rm -i *.txt

3. Using printf to Answer Multiple Questions

Sometimes, we may need to write a script that answers multiple prompts – for example, to automate software installation or cryptographic key generation.

If a script requires multiple inputs, we can simply use printf to answer them in the specified order.

3.1. A Sample Script That Requires Multiple Inputs

Let’s consider a bash script that asks three simple questions to the user:

#!/usr/bin/env bash

echo "Hello, please introduce yourself."

echo -n "Your name: "
read -r
name=$REPLY

echo "Are you human?"

echo -n "y/n: "
read -r
human=$REPLY

echo "What is your favorite programming language?"

echo -n "Your answer: "
read -r
lang=$REPLY

echo
echo "Answers:"
echo "1. $name"
echo "2. $human"
echo "3. $lang"

Let’s save our script into a file named questions.sh and run it:

$ chmod +x questions.sh
$ ./questions.sh
Hello, please introduce yourself.
Your name: Ziggy
Are you human?
y/n: y
What is your favorite programming language?
Your answer: Java

Answers:
1. Ziggy
2. y
3. Java

As we can see from the output above, our script asks us three questions and prints our answers afterward. Now, let’s try to automatically answer all the prompts in our script.

3.2. Piping printf to Our Script

We should output our answers in the correct order via the printf command with newline characters between them, and pipe them to our script:

printf "bot\nn\nJava" | ./questions.sh

If we run this command, we’ll see that the responses to the questions are “bot”, “n”, and “Java”, respectively. Note that we put newline characters between each answer to input them separately to three different prompts.

4. Using expect

Sometimes, we may need to interact with a program in a more complex way. If so, expect is a command-line application we can use to script custom logic that interacts with a program. expect acts as a script interpreter such as bash, and has its own syntax.

4.1. Writing a Simple expect Script

To wait for our prompts and answer them, we can use an expect script:

#!/usr/bin/expect -f

set timeout -1
spawn ../4/questions.sh

expect "Your name: "
send -- "expect\n"

expect "Are you human?\r
y/n: "
send -- "n\r"

expect "What is your favorite programming language?\r
Your answer: "
send -- "Java\r"

expect eof

If expect is not installed on our system, we might need to install it through a package manager for it to work. For example, in a Debian or Ubuntu system, we would use:

apt install expect

Let’s save this script into a file named expect-script.exp, and run it:

chmod +x expect-script.exp
./expect-script.exp

We’ll see that the inputs “expect”, “n”, and “Java” are registered in this order.

Let’s see what the expect script does in this instance:

  1. Because of the shebang (#!) defined in the first line, our shell tries to run the script with the interpreter located at the path /usr/bin/expect.
  2. If expect is installed and found at this location, it starts to process our script.
  3. It goes through the instructions, much like a script interpreter such as bash, in the order in which they appear in the script file. In this case, the instructions are to wait for specific text, and to answer prompts by using the send command of expect after these specific pieces of text are received.

4.2. Using autoexpect

autoexpect is a great way to automatically generate expect scripts. It does that by recording our answers through a run of the original application and transforming the recording into an expect script. To generate an expect script to answer the prompts of our program, we can use:

autoexpect questions.sh

This will run questions.sh and start recording our actions. After we input our answers to prompts manually, a script called script.exp is generated after our program finishes execution.

Let’s check the generated script:

#!/usr/bin/expect -f
#
# This Expect script was generated by autoexpect on Wed Aug 26 05:47:00 2020
# ...

set force_conservative 0  ;# set to 1 to force conservative mode even if
			  ;# script wasn't run conservatively originally
if {$force_conservative} {
    set send_slow {1 .1}
    proc send {ignore arg} {
	sleep .1
	exp_send -s -- $arg
    }
}
set timeout -1
spawn ../4/questions.sh
match_max 100000
expect -exact "Hello, please introduce yourself.\r
Your name: "
send -- "Ziggy\r"
expect -exact "Ziggy\r
Are you human?\r
y/n: "
send -- "y\r"
expect -exact "y\r
What is your favorite programming language?\r
Your answer: "
send -- "Java\r"
expect eof

As we can see, autoexpect generates expect commands and tries to detect which outputs to wait for before answering prompts.

5. Conclusion

In this tutorial, we’ve seen how to use different methods and tools such as printf, yes, and expect to answer interactive prompts in a bash script. We’ve also seen how to use autoexpect to generate expect scripts based on our actions that are recorded when we answer prompts manually.

guest
0 Comments
Inline Feedbacks
View all comments