The goal of the fourth homework assignment is to allow you to practice writing basic shell scripts. In this assignment, you will need to utilize variables, if statements, case statements, for loops, while loops, functions, traps, and other basic bourne shell language features.

For this assignment, record your scripts and any responses to the following activities in the in the homework04 folder of your assignments Bitbucket repository and push your work by 11:59 PM Friday, February 19, 2016.

#!/bin/sh

Regardless of which interactive shell you use, for the purposes of this class, we will use the bourne shell (ie. /bin/sh), which is the most basic and most portable Unix shell, as our scripting language.

Note, that the bourne shell is different from the bourne again shell (ie. /bin/bash). The latter can be considered a superset of the original bourne shell and thus backwards compatible with it. Although many of readings will discuss things in terms of the bourne again shell, we will only focus on the bourne shell subset.

Activity 01: Bake (5 Points)

For the first activity, you are to create a shell script, bake.sh, that automatically compiles every source code file in a folder into an executable.

Task 1: bake.sh

The bake.sh script takes no arguments. Instead it searches the current directory for files that match any the suffixes in the environment variable SUFFIXES. By default, SUFFIXES should be .c and thus match any files that end in .c.

For each of these source files, the bake.sh should compile the source file into an executable. The compiler should be set by the environment variable CC and the flags specified by CFLAGS. By default, CC should be set to gcc and CFLAGS should be -std=gnu99 -Wall.

Should the compilation command fail while compiling all the files that match the SUFFIXES, then the script should immediately exit with an non-zero status code.

Additionally, if VERBOSE is set, then the bake.sh script should emit the command that is being used to compile the source file into an executable.

Here are some examples of bake.sh in action:

# Don't worry about the case where there are no source files
$ ls
bake.sh

# Compile source file
$ ./bake.sh
gcc: error: *.c: No such file or directory

# Create source file
$ cat > hello.c <<EOF
#include <stdio.h>
int main(int argc, char *argv[]) {
    puts("Hello, World!");
    return 0;
}
EOF

# Compile source file
$ ./bake.sh

# Run compiled program
$ ./hello
Hello, World!

# Compile source file (overriding VERBOSE)
$ env VERBOSE=1 ./bake.sh
gcc -std=gnu99 -Wall -o hello hello.c

# Run compiled program
$ ./hello
Hello, World!

# Rename files
$ mv hello.c hello.cc

# Compile source files (overriding CC, CFLAGS, SUFFIXES, VERBOSE)
$ env CC=g++ CFLAGS="-g -Wall" SUFFIXES=.cc VERBOSE=1 ./bake.sh
g++ -g -Wall -o hello hello.cc

# Run compiled program
$ ./hello
Hello, World!

Hints

Task 2: test_bake.sh

To aid you in testing the bake.sh script, we are providing you with test_bake.sh which you can use as follows:

# Download script
$ curl -O http://www3.nd.edu/~pbui/teaching/cse.20189.sp16/static/sh/test_bake.sh

# Make script executable
$ chmod +x test_bake.sh

# Run test script
$ ./test_bake.sh
bake.sh test successful!

Test Scripts

Note, the test scripts are meant as a smoke test but are not considered exhaustive or comprehensive. That is, if there is a failure during testing then that indicates that something is most likely wrong with your script. On the other hand, if the test script succeeds, then you can be confident in the functionality of your script, but it is not guaranteed it is 100% correct.

Task 3: README.md

In your README.md, answer the following questions:

  1. Describe how you implemented the bake.sh script. In particular, briefly discuss:

    a. How you handled setting variables and the default values.

    b. How you iterated over all the files that match the SUFFIXES.

    c. How you handled the VERBOSE variable.

    d. How you terminated the program early if the compilation command failed.

  2. Compare using bake.sh to make. What are the advantages and disadvantages of both automated building techniques? What will you use in the future (bake.sh, make, or something else)?

Activity 02: Disk Usage (5 Points)

For the second activity, you are to create a script, disk_usage.sh, that given a directory, the script lists the n largest directories or files.

Task 1: disk_usage.sh

The disk_usage.sh script takes two possible flags and then a list of directories:

$ ./disk_usage.sh
usage: disk_usage.sh [-a -n N] directory...

The -a flag means that the script should list both files and directories, and the -n flag indicates that only the top N entries should be printed (if N is not specified, then it should default to 10).

Here are some examples of disk_usage.sh in action:

# Run on /etc
$ ./disk_usage.sh /etc
15M     /etc/
6.5M    /etc/udev
2.0M    /etc/ssl
1.9M    /etc/ssl/certs
1.8M    /etc/ca-certificates/extracted
1.8M    /etc/ca-certificates
1.1M    /etc/pacman.d/gnupg
1.1M    /etc/pacman.d
780K    /etc/ca-certificates/extracted/cadir
340K    /etc/ssh

# Run on /etc (limit output to top 5)
$ ./disk_usage.sh -n 5 /etc
15M     /etc
6.5M    /etc/udev
2.0M    /etc/ssl
1.9M    /etc/ssl/certs
1.8M    /etc/ca-certificates/extracted

# Run on /etc (include files)
$ ./disk_usage.sh -a /etc
15M     /etc
6.5M    /etc/udev/hwdb.bin
6.5M    /etc/udev
2.0M    /etc/ssl
1.9M    /etc/ssl/certs
1.8M    /etc/ca-certificates/extracted
1.8M    /etc/ca-certificates
1.1M    /etc/pacman.d/gnupg
1.1M    /etc/pacman.d
780K    /etc/ca-certificates/extracted/cadir

# Run on /etc (include files, limit output to top 5)
$ ./disk_usage.sh -a -n 5 /etc
15M     /etc
6.5M    /etc/udev/hwdb.bin
6.5M    /etc/udev
2.0M    /etc/ssl
1.9M    /etc/ssl/certs

# Run on /etc and /var (limit output to top 2 per directory argument)
$ ./disk_usage.sh -n 2 /etc /var
15M     /etc
6.5M    /etc/udev
7.8G    /var
4.1G    /var/log/journal/4e5d9581840047019e266fb2123b1c90

Hints

Task 2: test_disk_usage.sh

To aid you in testing the disk_usage.sh script, we are providing you with test_disk_usage.sh which you can use as follows:

# Download script
$ curl -O http://www3.nd.edu/~pbui/teaching/cse.20189.sp16/static/sh/test_disk_usage.sh

# Make script executable
$ chmod +x test_disk_usage.sh

# Run test script
$ ./test_disk_usage.sh
disk_usage.sh test successful!

Task 3: README.md

In your README.md, answer the following questions:

  1. Describe how you implemented the disk_usage.sh script. In particular, briefly discuss:

    a. How you parsed the command line arguments.

    b. How you handled the case where there are no command line arguments.

    c. How you processed each directory argument.

    d. How you incorporated the command line arguments into the commands you used to compute the top N items in each directory.

  2. Discuss what was the hardest part about this script and why. Additionally, identify what part of the program took up the most amount of code. Is this surprising? Why or why not?

Activity 03: Taunt (5 Points)

For the final activity, you are to create a script taunt.sh, which is your own version of the SLEEPER from Homework 02.

Task 1: taunt.sh

The taunt.sh script, like the SLEEPER, takes no command line arguments. When executed, it should emit a message using cowsay, which can be found in the following directory on the student machines:

/afs/nd.edu/user15/pbui/pub/bin

After the initial message, the taunt.sh script should handle three cases:

  1. If it receives no signals after 10 seconds, then the taunt.sh script should emit a witty message and exit.

  2. If it receives a SIGHUP signal, then it should emit a special message and exit.

  3. If it receives a SIGINT or SIGTERM, then it should emit a taunt and exit.

These scenarios are demonstrated below. The content of the messages are completely up to you. Have fun... but keep it clean (or not!) :].

Hints

Task 2: test_taunt.sh

To aid you in testing the taunt.sh script, we are providing you with test_taunt.sh which you can use as follows:

# Download script
$ curl -O http://www3.nd.edu/~pbui/teaching/cse.20189.sp16/static/sh/test_taunt.sh

# Make script executable
$ chmod +x test_taunt.sh

# Run test script
$ ./test_taunt.sh
Testing timeout...
_______________________________
< Uh... What? What do you want? >
-------------------------------
  \
    \
        .--.
      |o_o |
      |:_/ |
      //   \ \
    (|     | )
    /'\_   _/`\
    \___)=(___/

___________________________________
/ Ugh... I'm going back to sleep... \
\ ZzZzZ...                          /
-----------------------------------
  \
    \
        .--.
      |o_o |
      |:_/ |
      //   \ \
    (|     | )
    /'\_   _/`\
    \___)=(___/

Testing SIGTERM...
_______________________________
< Uh... What? What do you want? >
-------------------------------
  \
    \
        .--.
      |o_o |
      |:_/ |
      //   \ \
    (|     | )
    /'\_   _/`\
    \___)=(___/

___________________________________
/  WTF are you trying to do here?   \
| Terminate me?                     |
|                                   |
| Get lost, pbui! I'm going back to |
\ sleep!                            /
-----------------------------------
  \
    \
        .--.
      |o_o |
      |:_/ |
      //   \ \
    (|     | )
    /'\_   _/`\
    \___)=(___/

Testing SIGHUP...
_______________________________
< Uh... What? What do you want? >
-------------------------------
  \
    \
        .--.
      |o_o |
      |:_/ |
      //   \ \
    (|     | )
    /'\_   _/`\
    \___)=(___/

________________________________________
/  Hmm... I recognize you pbui... Here's \
| the message you need to give to the    |
| ORACLE:                                |
|                                        |
\ Y29odj0xNDU1NDAyODA2                   /
----------------------------------------
  \
    \
        .--.
      |o_o |
      |:_/ |
      //   \ \
    (|     | )
    /'\_   _/`\
    \___)=(___/

Note: Unlike the previous two tests, this script merely runs your taunt.sh script and tries to interact with it. It does not check whether or not it worked (because it has no way of knowing your messages will be), so this is simply a way for you to automate running your taunt.sh through the basic scenarios.

Task 3: README.md

  1. Describe how you implemented the taunt.sh script. In particular, briefly discuss:

    a. How you handled different signals.

    b. How you passed long messages to cowsay.

    c. How you handled the timeout.

  2. Compare writing shell scripts to writing C programs. Which one is easier? Which one do you prefer? When would you use one over the other?

Guru Point (1 Point)

For extra credit, you are to build a package for your favorite Linux distribution. The package can be anything: a new program, a set of icons, a collection of wallpaper, etc.

Here are some links to help you get started:

To get credit, you must show either a TA or the instructor a demonstration of you building and installing the package you created.

Feedback

If you have any questions, comments, or concerns regarding the course, please provide your feedback at the end of your README.md.

Submission

To submit your assignment, please commit your work to the homework04 folder in your assignments Bitbucket repository by 11:59 PM Friday, February 19, 2016. Your homework04 folder should only contain the following files: