For user input we make use of the read command.
The read command is available under all shells - it's a shell built-in command. On a side note, if you want to know whether a command is built-in or not, you can 'type' it as follows:
type read |
Which should respond with:
read is a shell builtin |
What about:
type type |
You will see that this is also a built-in. Try:
type ls |
which should tell you that ls is a command that has been aliased:
ls is aliased to `ls --color=tty' |
Back to the read command. If you type the following on the command line:
read X Y |
You will notice that the shell stares blankly back at you. It's actually waiting for two values for the variables X and Y to be entered by you, so go right ahead and satisfy it!
Type in
12 24 |
You're returned to the prompt, now type:
echo $X $Y |
and you should see the values that you entered for those variables. If somebody runs our eatout.sh script without any parameters:
./eatout.sh |
Then we could assume that it is being run in interactive mode. We want to add:
read TYPE RATING |
to our script, and then perform the rest of our script based on those two parameters.
In our script, we could add:
echo "Oh, you like $TYPE food" echo "here are the restaurants I rate:" |
Now if we want to choose restaurants according to a rating we could:
grep "$RATING" restaurants.txt | grep "$TYPE" |
Clearly this is only going to return the restaurants with the rating you have requested, none that have a higher rating.
This might not be quite what you want.
Instead, you want all restaurants that have got a rating equal to or higher than whatever rating you entered. But for now, let's live with the former - I'll leave the latter to you as an exercise.
Since we now know about CASE statements, we may want to use one here:
read type for type in ... do case type in italian|pizza|pasta) ... ... *) echo "Sorry enter a restaurant we like" done |
We will spend some time putting this together in our exercises at the end of this chapter.
Okay, so the read command allows us to offer a prompt, but clearly the user doesn't know what is expected of them, unless we tell them. We could achieve this as follows:
echo "Please enter the type and rating of your restaurant choice" read TYPE RATING |
At least now the user knows that she must enter a TYPE and a RATING which will make more sense. We've got another option though, namely, the -p flag:
read -p |
This allows one to include a prompt as can be seen below:
read -p "Enter two numbers (12 3):" X Y |
This would prompt for two numbers that must be entered.
Notice that read automatically assigns the values to these variables. In our example above, X and Y are the variable names used.
Reading a users name may entail:
read -p "Enter your name" NAME echo $NAME |
The echo would print the NAME variable, as it is entered at the prompt
Read can be used in another very useful way: it allows one to read an entire line. Let's say you have a file of restaurants, ratings, etc. as before, and you're wanting to read this entire file in order to swap the rating and the restaurant type. We saw one way to achieve this with sed earlier in the course, but it involved some complex RE's. Let's try another method by using the 'while' loop.
An example of an entry in the file is:
rating,type,restaurant name,telephone number 5,italian,Butlers,6867171 |
with every field being separated by commas.
Why commas? Suppose I had an entry as follows in my restaurants.txt:
10,smart,Boschendal Restaurant,88616 |
Where the restaurant name has a space within it. As the default field separator is a space, if I didn't use commas as a delimiter, then read would interpret this line incorrectly as it would consider that the above line has 5 fields, rather than 4.
To avoid this potential problem, and allow me to keep 'Boschendal Restaurant' as a space separated field, I have ensured the space-separated fields are now comma separated.
On another note, CSV (comma separated value) files are common ways of transferring data between one spreadsheet and another.[20]
Now, in order to swap the columns, I could use the while and the read together as follows:
IFS="," while read RATING TYPE PLACE TEL do echo "$type,$rating,$place,$tel" done < restaurants.txt |
This will repeatedly read in a line of text from the file restaurants.txt, until there are no more lines left, at which point the read will return a FALSE (1).
On reading each line, the fields are automatically placed into the variable names, making swapping them a trivial exercise! No more writing complex RE's, no more fussing about!
One last point. You will notice that prior to starting the while loop, I change the input field separator (IFS) to be a comma. By default, IFS=$' \t\n', a space, a TAB and a NEWLINE.
I need to change this to accommodate my commas in the file, so IFS="," will solve this problem. Without this change, the 'while read ...' will not produce the desired output
Look at the exercises for other ways to employ your read.
Now, back to building our menu system. An example of a menu system may be this pseudo-code:
while read RATING TYPE do case RATING in [0-9]: do x,y,z ;; * : echo "Sorry, please enter a rating between 0 and 9" continue ;; esac case TYPE in .... .... .... esac .... .... .... done |
[20] Pipe your restaurant.txt through sed and replace your spaces by commas, so that you end up with a file that looks like mine.