- Unix Shell Scripting
- Shell Basics
- Testing in bash
- Capturing User Input
- Scripting : Exercise 1
- Bourne/Bash/Korn Commands
- Shell Variables
- IO Redirection
- Operators, Wildcards and Expressions
- Flow Control
- Scripting : Exercise 2
- Shell Differences
- String Functions
- Power Tools
- 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:
2. Create a text file called numbers in your home directory. It will contain six telephone numbers, similar to:
(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:
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:
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
Now cat the phonebook file:
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:
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.