variable
is the name of one or more variables used to hold the input value. If no variable name is supplied, the shell variable REPLY
contains the line of data.-d
delimiter
The first character in the string delimiter
is used to indicate end of input, rather than a newline character.
-e
Use Readline to handle input. This permits input editing in the same manner as the command line.
-n
num
Read num
characters of input, rather than an entire line.
-p
prompt
Display a prompt for input using the string prompt
.
-r
Raw mode. Do not interpret backslash characters as escapes.
-s
.
Using the various options, we can do interesting things with read
. For example, with the -p
option, we can provide a prompt string:
With our new ability to have keyboard input comes an additional programming challenge: validating input. Very often the difference between a well-written program and a poorly written one lies in the program’s ability to deal with the unexpected. Frequently, the unexpected appears in the form of bad input. We did a little of this with our evaluation programs in the previous chapter, where we checked the values of integers and screened out empty values and non-numeric characters. It is important to perform these kinds of programming checks every time a program receives input to guard against invalid data. This is especially important for programs that are shared by multiple users. Omitting these safeguards in the interests of economy might be excused if a program is to be used once and only by the author to perform some special task. Even then, if the program performs dangerous tasks such as deleting files, it would be wise to include data validation, just in case.
Here we have an example program that validates various kinds of input:
#!/bin/bash # read-validate: validate input invalid_input () { echo "Invalid input '$REPLY'" >& 2 exit 1 } read -p "Enter a single item > " # input is empty (invalid) [[ -z $REPLY ]] && invalid_input # input is multiple items (invalid) (( $(echo $REPLY | wc -w) > 1 )) && invalid_input # is input a valid filename? if [[ $REPLY =˜ ^[-[:alnum:]\._]+$ ]]; then echo "'$REPLY' is a valid filename." if [[ -e $REPLY ]]; then echo "And file '$REPLY' exists." else echo "However, file '$REPLY' does not exist." fi # is input a floating point number? if [[ $REPLY =˜ ^-?[[:digit:]]*\.[[:digit:]]+$ ]]; then echo "'$REPLY' is a floating point number." else echo "'$REPLY' is not a floating point number." fi # is input an integer? if [[ $REPLY =˜ ^-?[[:digit:]]+$ ]]; then echo "'$REPLY' is an integer." else echo "'$REPLY' is not an integer." fi else echo "The string '$REPLY' is not a valid filename." fi
This script prompts the user to enter an item. The item is subsequently analyzed to determine its contents. As we can see, the script makes use of many of the concepts that we have covered thus far, including shell functions, [[ ]]
, (( ))
, the control operator &&
, and if
, as well as a healthy dose of regular expressions.
A common type of interactivity is called menu driven. In menu-driven programs, the user is presented with a list of choices and is asked to choose one. For example, we could imagine a program that presented the following:
Please Select: 1. Display System Information 2. Display Disk Space 3. Display Home Space Utilization 0. Quit Enter selection [0-3] >
Using what we learned from writing our sys_info_page
program, we can construct a menu-driven program to perform the tasks on the above menu:
#!/bin/bash # read-menu: a menu driven system information program clear echo " Please Select: 1. Display System Information 2. Display Disk Space 3. Display Home Space Utilization 0. Quit " read -p "Enter selection [0-3] > " if [[ $REPLY =˜ ^[0-3]$ ]]; then if [[ $REPLY == 0 ]]; then echo "Program terminated." exit fi if [[ $REPLY == 1 ]]; then echo "Hostname: $HOSTNAME" uptime exit fi if [[ $REPLY == 2 ]]; then df -h exit fi if [[ $REPLY == 3 ]]; then if [[ $(id -u) -eq 0 ]]; then echo "Home Space Utilization (All Users)" du -sh /home/* else echo "Home Space Utilization ($USER)" du -sh $HOME fi exit fi else echo "Invalid entry." >&2 exit 1 fi
This script is logically divided into two parts. The first part displays the menu and inputs the response from the user. The second part identifies the response and carries out the selected action. Notice the use of the exit
command in this script. It is used here to prevent the script from executing unnecessary code after an action has been carried out. The presence of multiple exit points in a program is generally a bad idea (it makes program logic harder to understand), but it works in this script.
In this chapter, we took our first steps toward interactivity, allowing users to input data into our programs via the keyboard. Using the techniques presented thus far, it is possible to write many useful programs, such as specialized calculation programs and easy-to-use frontends for arcane command-line tools. In the next chapter, we will build on the menu-driven program concept to make it even better.
It is important to study the programs in this chapter carefully and have a complete understanding of the way they are logically structured, as the programs to come will be increasingly complex. As an exercise, rewrite the programs in this chapter using the test
command rather than the [[ ]]
compound command. Hint: Use grep
to evaluate the regular expressions, and then evaluate its exit status. This will be good practice.