Scripting : Exercise 1

  1. Unix Shell Scripting
  2. Shell Basics
  3. Testing in bash
  4. Capturing User Input
  5. Scripting : Exercise 1
  6. Debugging
  7. Bourne/Bash/Korn Commands
  8. Shell Variables
  9. IO Redirection
  10. Pipes
  11. Operators, Wildcards and Expressions
  12. Flow Control
  13. Scripting : Exercise 2
  14. Shell Differences
  15. String Functions
  16. awk
  17. xargs
  18. Power Tools
  19. Exercise 3

If you’re logged in as root on a local machine, it’s time to log out for safer scripting. Re-log in as a normal user on your local Linux computer, or as your UNM account name if you’re using ssh.

1. Create a file called names. It must be a text file, in your home directory, with the following contents:

Flintstone, Fred
Rubble, Barney
Jetson, George
Sprockets, Spacely
Bunny, Bugs
Rubble, Betty

2. Create a text file called numbers in your home directory. It will contain six telephone numbers, similar to:

255-1111
255-2222
255-3333
255-4444
255-5555
255-2222

(Remember, Barney and Betty are married still, we hope.) You need not copy these numbers exactly, but there must be the same number of numbers as names.

3. Now it’s time to use the paste command. paste joins two files line-by-line; that is, it pastes corresponding lines together. Use this command:

paste names numbers > phonebook

Now you have a new file, phonebook. To see the result, command:

cat phonebook

Note that there is a tab character (inserted by paste) between the two columns.

4. paste will assemble a new file composed of the “pasted” lines of two (or more) old files. The cut command can cut this file back apart. cut can cut based on a number of characters:

cut -c1 phonebook

which returns just the first character from each line, while

cut -c1-18 phonebook

returns characters 1-18 from each line. You can also cut by tab-delimited fields:

cut -f1 phonebook

returns the first field, i.e. the name field, from phonebook. If the delimiter isn’t a tab (say, it’s a colon), you can tell cut with the -d option:

cut -d: -f1 phonebook

would give you the first field of phonebook if it was colon-delimited instead of tab-delimited. Like most commands, cut spills its output to the screen if you don’t redirect it back into another file. Try all these options.

5. Let’s make a lookup script called lu. After the shebang, it should read:

grep "$1" phonebook

The special variable $1 means “the first argument passed to lu.” Save the script and close it.

Call it thusly:

./lu Betty

In this case, we’re making the lu script return the line that contains the name Betty, which incidentally also contains her phone number. Voila – we’ve got a lookup script! Try it a few times.

6. Now we need a way to add numbers to our phone book. Create another script called add. After the shebang, it should read:

echo -e "$1\t$2" >> phonebook
# -e enables the escape character \
# \t is a tab character
sort -o phonebook phonebook

We’ve set ourselves up to add names and numbers to phonebook, with a tab between them, just like paste would use. Notice the sort command. By default, it sorts the lines of a file alphabetically, by the first field. You do have to use a special notation (note that -o option) to send the sorted output back to the originating file. The command:

sort phonebook > phonebook #WRONG!

will NOT work!

7. Next, we’ll build a script to remove listings. Start a new script called rem. After the shebang, it will read:

grep -v "$1" phonebook > /tmp/phonebook

mv /tmp/phonebook phonebook

The grep command with the -v option returns all lines from phonebook that do NOT contain the name we passed through the $1 variable. We have to redirect the output to a temporary file, then move it back to the original location, replacing the original file. Debug as necessary.

Give this script a try: run the command

./rem Fred

Now cat the phonebook file:

cat phonebook

It works, right? Now try:

./rem Spacely Bugs

What’s the problem with this?

8. Okay, we need to correct this issue. Copy rem to rem2 so we can try again. Just under the shebang, add:

if [ "$#" -ne 1 ] ; then
echo "Supply 1 and only 1 name to delete."
exit 5
fi

There’s a lot going on here! First, we’re using the special variable $# to get the number of arguments passed to rem2. If there’s any number of arguments but one, the user gets the echoed error message. Then rem2 exits with an error code of 5, without executing any of the following code.

If there’s only one argument passed with the command, rem2 does the same thing as rem.

However, what happens if we issue the command:

./rem2 Rubble

9. So we have to make sure we’re not deleting more than one entry at once.

Copy rem2 to rem3.

Open rem3 and delete all the code after the shebang.

Replace it with this:

#Make sure we match only 1 name
matches=$(grep "$1" phonebook | wc -l)
#that's an "el," not a "one," at the end
if [ "$matches" -gt 1 ] ; then
echo "More than one name matches. Please be more specific."
grep -i "$1" phonebook
exit 6
#notice we supply a second error code
elif [ "$matches" -eq 1 ] ; then
#find and remove the listing
grep -v "$1" phonebook > /tmp/phonebook
#restore the file
mv /tmp/phonebook phonebook
echo "Success! $1 deleted."
else
echo "$1 isn't in the phone book."
exit 7 #here's our third error code
fi

Now do some testing on rem3.

10. The rem script needs one more revision to catch instances when more than one name is supplied.

Your assignment is to write it.