ANU The Australian National University



____________________________________________________

[ANU] [DCS] [COMP2100/2500] [Description] [Schedule] [Lectures] [Labs] [Homework] [Assignments] [COMP2500] [Assessment] [PSP] [Java] [Reading] [Help]

____________________________________________________

COMP2100/2500
Lab 4: Shell programming

Summary

You will get some experience interacting with the shell and writing some simple bash scripts.

Aims

Preparation


Exercise 1

Open a terminal window and enter the following command:

[comp2100@partch]$ echo Hello world

You should see the response Hello world appear as a result.

That command was interpreted by tcsh, your default shell. For these exercises we want to use the bash shell. To start bash just enter the command bash. When you do this, the prompt will change to something ending with a ‘$’, the default prompt for all shells in the Bourne shell family.

Enter the command to print ‘Hello world’ again, this time to bash. You should get the same result. One shell is pretty much like another when processing simple commands.

You can exit bash either by typing the command exit, or by typing Ctrl-D (hold down the Control key and press d). In UNIX, Ctrl-D signals the end of input.


Exercise 2

Create a directory called bin in your home directory (if you don’t already have one). Don't use the File Manager GUI to do this, use the ‘mkdir’ command. If you don't know how it works, look up the manual page by typing ‘man mkdir’. This directory should be one place your shell will go looking for commands that are not built in to the shell. If you write a program of your own that you want to use on a regular basis, just copy it to this directory. Then, whenever you type the name of your program as a command, the shell will find it here and run it. (For a complete list of all the places the shell will look, known as a ‘search path’, enter the command echo ${PATH}. Do this now to check that your bin directory is on your path.)

Create a file called hello in your bin directory with the following contents:

#!/bin/bash

echo Hello world

Next set the executable bit on this file with the command ‘chmod +x hello’.

You should now be able to run your new command by typing its name ./hello. You don't have to be in the same directory as your command for this to work. Try moving to another directory and typing hello again.


Exercise 3

Make a new directory (say comp2100/labs/lab4 or something like that) and download the following Java program StringCount.java into it. Compile the program by typing

javac StringCount.java

This program takes two string arguments from the command line and returns a count of how many times the first string can be found in the second. Occurrences must be contiguous; for example, ‘cat’ does not occur in ‘cxaxt’. Occurrences may overlap, for example ‘wowow’ occurs twice in ‘wowowow’.

Well, that's what it's supposed to do. In reality, it doesn't come close to working. Try it out by typing

java StringCount abc abcd

You should get the answer ‘1’, but you won't.

It's going to take a long time to test this program if we have to enter each test case by hand, and re-enter it by hand after every modification. Chances are we'll end up making as many mistakes as the program. Instead, let's write a shell script to do it. Start by creating a file called cases to contain your test cases. Each line should contain two strings. Here are a few lines to get you started. Add some more of your own.

x x
x xx
wowow wowowowow

The following simple shell script will read two words from each line of its input and print those words out again.

#!/bin/bash

while read x y
do
  echo ${x} ${y}
done

The key feature of the script is the read command, which we didn't cover in lectures. It reads a line of input and assigns the first word to the variable x and the rest of the line to the variable y. The read command gives an exit code of 0 (= success = True) if was was able to read the words, or 1 (= failure = False) if it encounters the end of its input. That makes it perfect for using in a while loop, as in this example. When the input runs out, it returns 1, and the loop terminates.

Save this script in a file called tester, make it executable, and run it over your test input by typing ./tester < cases.

Hint: When editing shell scripts you may find it helpful to put Emacs into ‘shell-script-mode’. If you edit an existing script, this should happen all by itself, but when you start a new script you need to tell Emacs to change mode by typing M-x shell-script-mode. (The M stands for ‘Meta’, and it works just like Shift or Control: hold down the Alt key while you type an ‘x’.)


Exercise 4

Modify tester so that for each input line of the form ‘string1 string2’, it outputs the number of occurrences of string1 that StringCount claims are in string2.

Note: This does not mean rewrite the Java program as a bash script. It means write a bash script that runs the Java program on each line of its input and formats the results.


Exercise 5

Save the current version of your script and your cases file for later. (You will need these again in Exercises 8 and 9.) Do this by copying tester to tmp_tester and cases to tmp_cases using the cp command.

Modify your cases file so that each line now contains 3 things, the two words as before, plus the correct number of occurrences of the first word in the second. For example, your file will start:

x x 1
x xx 2
wowow wowowowow 3

Modify your tester script so that for each test it prints a line of the following form:

Test i: string1 string2: Expected n, got m

where i is the line number of this test, n is the correct number of occurrences of string1 in string2, and m is the number found by StringCount.


Exercise 6

Modify your tester so that when strcnt actually gets the right answer (a rare occurrence, but you should be able to find one), then it instead prints a line of form:

Test i: Passed

The key to this (unsurprisingly) is to use an if statement in your shell script. Remember that the form of an if statement is as follows:

if command-list
then
    command-list
else
    command-list
fi

Note: The line breaks (or semicolons in their place) are required. You can leave out the else part if you don't need it.

Have a look at the manual page for the test command (by typing man test). That should tell you everything you need to know now to complete this exercise. The test command evaluates a boolean expression (as well as being able to test for all sorts of other conditions). It basically lets you program in Bash in a way very like what you would do in Java.


Exercise 7

The test command is so commonly used in shell scripts that it has a short-hand notation in which [ stuff ] means the same as test stuff (those spaces inside the square brackets are required). Use this notation to simplify your solution to Exercise 6.


Exercise 8

For this exercise you should return to using a version of your test cases file where each line just contains the two strings to be tested. Make a second file called expected that contains just the correct answers for your test cases, one per line. Now, create a new tester script that writes the output from each of your tests to a file called results.

Hint 1: The line command > file redirects the output of command into file. Similarly, command >> file appends the output of command to the existing content of file (creating it if it did not previously exist).

Hint 2:: You may wish to solve this problem by first removing the file results and then adding output to it one line at a time. Check out the manual page for the rm command to find options that will cause it to work cleanly even when the file to be removed does not exist.


Exercise 9

Create another version of the tester script that works like the one from Exercise 8, but then uses diff to check if the results produced are the same as the expected answers. If yes, have your script print ‘All tests passed’, otherwise have it print ‘Some tests failed’.

Hint: Output redirected to the special file /dev/null will never be seen again.


Exercise 10

How many Java source files do you have in your file space?

Hint 1: Commands can tell if their output has been redirected to a file or a pipe, and can change their behaviour as result. Check out the difference between the output of ls and ls | cat. The cat command just copies its input to its output, so any differences have come about because ls sensed it was connected to a pipe and modified its behaviour. Why would it do that?

Hint 2: You should be able to complete this exercise by using only the commands ls, wc, and grep.

Hint 3: Check out the -R option on ls and all the options on wc. (Use the man command.)

Hint 4: The grep pattern '\.java$' (those quote marks are important) will match lines ending in .java.


Exercise 11*

Starred exercises are extension work, and should not be attempted until you have solved all the regular exercises. They are optional, but remember that the more you do, the more you will learn.

So, where are all these Java files then? Just using ls and grep can get you the names of all your Java files, but it won't tell you where to find them. Read the manual page for the find command, and then use it to print the locations of all your Java files.


Exercise 12*

Fix the StringCount program and check your fixes by using the testing script and test data you developed earlier.

____________________________________________________

[ANU] [DCS] [COMP2100/2500] [Description] [Schedule] [Lectures] [Labs] [Homework] [Assignments] [COMP2500] [Assessment] [PSP] [Java] [Reading] [Help]

____________________________________________________

Copyright © 2005, Jim Grundy & Ian Barnes, The Australian National University
Version 2005.3, Tuesday, 26 April 2005, 15:26:36 +1000
Feedback & Queries to comp2100@cs.anu.edu.au