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.
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.
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.
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!
You can use basename to remove the SUFFIXES
.
To allow environment variables to override variables defined inside the
script, you can either use an if
statement to conditionally set the
variable or you can use parameter expansion.
To terminate the script early when a compilation fails, you can either use
an if
statement or short circuit evaluation.
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!
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.
README.md
In your README.md
, answer the following questions:
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.
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)?
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.
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
To parse the command line options, you should use getopts.
To suppress extraneous error messages from du, you should redirect
stderr to /dev/null
.
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!
README.md
In your README.md
, answer the following questions:
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.
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?
For the final activity, you are to create a script taunt.sh
, which is your
own version of the SLEEPER from Homework 02.
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:
If it receives no signals after 10
seconds, then the taunt.sh
script
should emit a witty message and exit.
If it receives a SIGHUP
signal, then it should emit a special message
and exit.
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!) :].
You may wish to adjust the PATH
environment variable inside your script
to include the directory containing cowsay.
cowsay comes with a number of figures... some NSFW.
You may wish to create functions for handling signals.
You may wish to sleep multiple times rather than doing one long sleep.
You may wish to use here documents to construct long messages.
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.
README.md
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.
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?
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.
If you have any questions, comments, or concerns regarding the course, please
provide your feedback at the end of your README.md
.
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:
README.md
bake.sh
disk_usage.sh
taunt.sh