Running interactive programs uninteractively in Bash scripts
Bash scripts are great for automating tasks. Some challenges arise though when you want to automate a process that involves programs that ask for user input by keyboard. You know, the kind of program that keeps asking things like: "Are you sure you want to do this (y/n)?". Most programs accept arguments that enable some kind of "non-interactive mode" for that. For example,
asks for confirmation before deleting somefile, while
rm -i somefile
does not. This of course makes the latter a lot more suitable for use in automated scripts. Unfortunately, not all programs provide these kind of arguments. What to do with them?
rm -f somefile
One option is to use redirection and echo the input into them, like so:
or, if you want to remove a lot of files (and for some reason don't want to replace the -i argument with -f), with the brilliant command yes:
echo "yes" | rm -i somefile
yes | rm -i somefile*
Things start to look ugly though when you have to provide a lot of different input texts. Say you have a program generate_random_files which, well, generates random files after asking in sequence:
- How many files?
- Size of files?
- Are you sure (y/n)?
but it doesn't look particularly readable. Another option would be to create a text file input.txt with contents:
/bin/echo -e "2\n4\ny" | generate_random_files
and then invoke the program with redirected input from this file:
2
4
y
generate_random_files < input.txt
This gives you one more file to manage though. Also, it makes it clumsy to use Bash variables in the input. For example, if you want a file size not of 4, but of some variable $calculatedSize, then you would have to manipulate the text file from within your script before calling generate_random_files.
An elegant way to do it is to use what is called "here documents", which allows you to basically put the literal contents of input.txt right in your script, eliminating the need for a separate file and allowing use of variables, while keeping things reasonably human-readable. The syntax is: program name followed by << followed by a unique delimiter of your own choosing, then on a new line the text that serves as input for the program, and then on a new line (and not preceded by any spaces or tabs!) the delimiter again. Like in this little script:
And here's the same example, but using a variable which holds the size in bytes, calculated based on the argument $1 that provides the size in kilobytes:
#!/bin/bash
echo "Starting generate_random_files..."
generate_random_files <<TextForInput
2
4
y
TextForInput
echo "Finished!"
#!/bin/bash
sizeInKb=$1
calculatedSize=$((1024*$sizeInKb))
echo "Starting generate_random_files..."
generate_random_files <<TextForInput
2
$calculatedSize
y
TextForInput
echo "Finished!"