/home2/phys210/<you>/a02
in which to store the files you submitted for the second assignment.1 You then used "chmod -R o-r"
to make it and all its contents
(including any possible subdirectories and their contents)
inaccessible to o (for others).
The second step will no longer be necessary, as we have set up
a utility which periodically restores the correct
ownership, group and permissions for the
/home2/phys210/
directory tree.
This week, create
/home2/phys210/<you>/a03
for your submissions on this assignment;
and so on for the rest of the course.
We won't remind you any more.
In the previous Assignment you learned how to customize your bash environment using the bash resource file .bashrc in your $HOME directory. By now you should have edited the template .bashrc file and the .aliases file it sources to suit your taste, including lots of personalized, idiosyncratic aliases that you find easier to remember (or more æsthetically pleasing) than the "raw" bash commands - which, while æsthetically offensive, are at least reasonably universal and thus worth remembering. This may impede your familiarization with bash, but for now, go ahead and indulge yourself with alias.
The goal of this Assignment is to make you reasonably familiar and comfortable with shell scripts and a few other important tools.
/home2/phys210/<you>/a03/
directory,
write a short bash script called "bck.sh"
that uses tar2 to make a compressed "backup" of your entire
/home2/phys210/<you>
directory tree
in a single file ~/HW.tar.gz
(note that this file should be in your $HOME directory).
Make a symbolic link to ~/HW.tar.gz
in your /home2/phys210/<you>/a03/
directory.
Here's what bck.sh should do:
First, if there is already a ~/HW.tar.gz
file,
gunzip (decompress) it to ~/HW.tar
-
then use tar to append to ~/HW.tar
any files in ~/HW/
(and its subdirectories)
that have changed since the last time
bck.sh was invoked - then gzip ~/HW.tar
back
to ~/HW.tar.gz
and you're done.
Use "man tar" to find out how to do the middle part.3 This is a valuable thing to know how to do.4
Here's a "minimalist" version of bck.sh:
Note that none of these commands require parameter parsing;
so the whole thing can be stuck together in one line
and aliased to a "bck" command in .aliases:
alias bck='cd /home2/phys210/$USER; gunzip HW.tar; tar -uf HW.tar .; gzip HW.tar'
Now, there are several "ringers" in this version:
#! /bin/bash
cd /home2/phys210/$USER
gunzip HW.tar.gz
tar -uf HW.tar .
gzip HW.tar
/home2/phys210/<you>/a03/
directory.
In that directory, use the convert command
supplied by ImageMagick to make files
P210logo.jpg, P210logo.png, P210logo.tif and P210logo.pdf
(plus any other graphics formats you are especially fond of).
Which file is the smallest?
Is it the one you expected?
Record (in /home2/phys210/<you>/a03/readme.txt
)
your answer and any comments.
If you want to make your own graphics (maybe a personal logo?),
try "gimp". But don't get too distracted by it!
I don't think many had any trouble with this. I found the file sizes were
Open Source programs often prefer .png,
but proprietary software seems to avoid it for some reason.
5023 P210logo.png
5456 P210logo.gif
7128 P210logo.tif
8091 P210logo.jpg
8316 P210logo.eps.gz
16840 P210logo.pdf
58514 P210logo.eps
76815 P210logo.pnm
Note that Encapsulated PostScript (.eps) files are huge,
but when gzipped they are often (not always!) smaller than .pdf files.
Now, "results may vary" inasmuch as some types of files are much better suited to
specific formats. For instance, .jpg is an efficient format for photographs,
and you would usually get huge files and poor results storing them in other formats.
~phys210/bin/fib.sh
to your own /home2/phys210/<you>/a03/
directory
and (using your favourite editor)
add comment lines
(any line starting with "#")
to explain what is happening at each step.
Be ridiculously thorough; it will pay off in the end.
Make sure that your copy of the file is executable
("chmod +x fib.sh") and check that
it still works like the original
despite all the comments you've added.5
See fib-annotated.sh
(Your browser won't [or shouldn't] display it, for security reasons.)
It looks like this:
#! /bin/bash
## Every bash shell script must start with the above 2 lines.
## Check whether the number of arguments ($#) is 2.
## If not, then print on the screen everything down to EOHELP...
if [ $# -ne 2 ] ; then
cat <<EOHELP
"Fibonacci numbers:"
USAGE: fib.sh fstart nn
Generates & prints out the Fibonacci number fstart
and the nn following Fibonacci numbers. If fstart
is not a Fibonacci number, prints an error message.
EOHELP
## ... and if $# IS = 2, AND the first argument ($1) is > 0, then...
elif [ $1 -gt 0 ] ; then
## Create a "counter" called $jj and set it = 1:
let "jj = 1"
## Create a variable called $flast and set it = 1:
let "flast = 1"
## Create $ff (the current candidate F-number) and set it = 1:
let "ff = 1"
## Create $fref and initialize it to the first argument ($1):
let "fref = $1"
## Create $nn and set it = the second argument ($2) + 1:
let "nn = $2 + 1"
## Print an empty line on the screen (for aesthetics):
echo " "
## Print other stuff on the screen -- note that the value of
## a parameter can be printed, but must be outside quotation
## marks, which must delimit all explicit text:
echo $nn" Fibonacci numbers starting with "$fref":"
echo " "
## Do the following stuff as long as $nn > 0, then stop:
while test $nn -gt 0
do
## If the current candidate F-number ($ff) is > the first argument, ...
if [ $ff -gt $fref ] ; then
## ... then the first argument must NOT be an F-number. Say so,
## then set $nn = 0 so that we'll exit:
echo " "
echo "Ahem! "$fref" is not a Fibonacci number!"
echo " "
let "nn = 0"
## If $ff is equal to $fref, print it on screen and decrement $nn by 1:
elif [ $ff -eq $fref ] ; then
echo " "$jj": "$ff
let "fref = $ff + $flast"
let "nn = $nn - 1"
## Every "if" structure is terminated by "fi":
fi
## Every time, increment $jj by 1, store $ff in $ftmp, add $flast to $ff
## and store $ftmp (the previous value of $ff) in $flast.
## Then jump back to the while statement and (if $nn > 0) go around again:
let "jj = jj + 1"
let "ftmp = $ff"
let "ff = $ff + $flast"
let "flast = $ftmp"
## Every "do" loop is terminated by "done":
done
echo " "
fi
So your script, which you should call fact.sh
and which should be in your /home2/phys210/<you>/a03/
directory,
should look for two arguments, and N,
so that (for example) if you invoke it with
"fact.sh 2 3" it should print out on your screen
"2 6 24 120" (not necessarily all on the same line).
This should be simple, eh?8 To keep it that way, let's forbid using
either of the first two factorials as .
Thus if you enter 1 as your first argument
you should get no response.
If you enter the wrong number of arguments, the script should print out a "USAGE: . . . " message analogous to the one in fib.sh explaining how it is supposed to be used.
Oh, and one final feature: if you enter a first argument that is not a factorial, your script should print out a polite but perhaps slightly sarcastic message informing the user that they have entered an invalid starting point.9
You may want to start with a simpler version that just prints out the first 10 or so factorials, to make sure you have that part right; then add the bells and whistles involving arguments.
The idea here was to take the fib.sh script you just finished annotating
(and which you therefore presumably understand, at least a little)
and make the fewest possible changes to adapt it into fact.sh --
ideally, making one small change at a time and running the script to see the effect.
This is why I went on about "copycat programming" in class,
but many people insisted on "doing it properly" -- first learning
all about bash shell programming and then writing their own
fact.sh "from scratch". This is admirable in principle,
but overwhelmingly difficult in practice.
Here is my fact.sh:
#! /bin/bash
if [ $# -ne 2 ] ; then
cat <<EOHELP
"Factorials:"
USAGE: fact fstart nn
Generates & prints out the factorial fstart
and the nn following factorials. If fstart
is not a factorial, prints an error message.
EOHELP
elif [ $1 -gt 0 ] ; then
let "jj = 1"
let "ff = 1"
let "fref = $1"
let "nn = $2 + 1"
echo " "
echo $nn" factorials starting with "$fref":"
echo " "
while test $nn -gt 0
do
if [ $ff -gt $fref ] ; then
echo " "
echo "Ahem! "$fref" is not a factorial!"
echo " "
let "nn = 0"
elif [ $ff -eq $fref ] ; then
echo " "$jj": "$ff
let "fref = $ff*($jj+1)"
let "nn = $nn - 1"
fi
let "jj = jj + 1"
let "ff = $ff*$jj"
if [ $jj -gt 21 ] ; then
let "nn = 0"
fi
done
echo " "
fi
~phys210/public_html/fib.php
does the same thing
as the bash shell script fib.sh,
but it does it online for anyone.
Try it out at http://www.physics.ubc.ca/~phys210/fib.php
and then convert it to make factorials
in a file called fact.php
in your own ~/public_html/p210/
directory.11
See comments above about "copycat programming".
PHP is a huge and versatile language that you couldn't possibly learn
in a few days, much less a few hours. I hope you were motivated to
read up on PHP, as you might find it a useful way to implement
simple tasks that are not very compute-intensive; it has the
advantage of elaborate formatting for output (limited only by
your knowledge of HTML and the not-necessarily-an-advantage
of being open to use by anyone on the Internet. If you do much PHP
programming, you will soon want to read up on authentication.
For this part I didn't explicitly suggest that you annotate
the fib.php script, but I did mention in a lecture that
it is a very good idea to sketch a flowchart
of the basic workings of a program, especially if you are about to
modify it to do something else.
It is also a good idea to
change as few things as possible between tests.
This is covered by the 11th Commandment
(for experimental scientists):
Thou shalt not change more than one independent variable at a time.
On hyper, PHP warnings and error messages are suppressed
for security reasons (they can give away the secret workings of
the site's PHP scripts, thus giving hackers a golden opportunity
to enter ingenious inputs that cause bad things to happen).
Unfortunately, this means that if you have any error in your PHP script,
it yields only a blank page. The dedicated PHP enthusiast
with Linux installed at home
can freely install the Apache web server with PHP,
turn on verbose warning/error messages and learn exactly
what is going wrong; but this is a substantial commitment
of time and effort, since Apache is a complex application.
Anyway, here is a link to my fact.php --
but you won't be able to see the actual PHP code by using View Page Source.
Do you see why? This would be a huge security risk!
All you can see is the HTML generated by the PHP script.
Just for you, here is the actual code:
<?php
// -- The following 2 lines retrieve values (if any)
// POSTed to fact.php:
$fref = $_POST["fstart"];
$nn = $_POST["nn"];
// -- Now we start up the HTML that will be sent to
// the user's Web browser:
echo "
<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">
<HTML>
<HEAD>
<TITLE>Factorials</TITLE>
<LINK REL=\"STYLESHEET\" HREF=\"http://musr.physics.ubc.ca/css/p210.css\">
</HEAD>
<BODY bgcolor=\"FFFFFF\">
<CENTER>
<H2 CLASS=\"fancy\"> Factorials: </H2>
";
// -- (Note that any embedded quotation marks must be 'escaped' as \"
// because echo used double quotes to delimit its output.)
// -- Now check to see if meaningful arguments are provided:
if ( $fref <= 0 || $nn < 1 ) { // No. Ask for some!
// Note that the user's answers are resubmitted to this script
// (i.e. it calls itself!):
echo "
Generate & print out <tt>fstart</tt>
and the following <tt>nn</tt> Factorials,
<BR>
unless <tt>fstart</tt> is <i>not</i> a Factorial,
in which case print an error message.
<P>
<FORM METHOD=\"POST\" ACTION=\"fact.php\">
Start with <tt>fstart</tt> =
<INPUT TYPE=\"text\" NAME=\"fstart\" SIZE=\"14\">;
print it
<BR>
and the next <tt>nn</tt> =
<INPUT TYPE=\"text\" NAME=\"nn\" SIZE=\"3\"> Factorials.
<P>
<INPUT TYPE=\"submit\" value=\"GO!\">
</CENTER>
</BODY>
</html>
";
exit; // Once the HTML is sent, exit and wait for user to hit GO!
}
// -- OK, we have some plausible arguments. Run the numbers:
// Anything starting with "$" is a variable name.
$jj = 1; // $jj is the current index. Start with 1.
$ff = 1; // $ff is the current Factorial. Start with 1*1.
$nn++; // Add 1 to # of factorials to print.
echo "$nn Factorials starting with $fref:<P>";
// -- Note that variable values can be embedded in HTML by echo.
// -- Let's make it pretty, too:
echo "<TABLE>";
while ( $nn > 0 ) {
// For debugging: echo "jj=$jj; ff=$ff; nn=$nn<BR>";
if ( $ff > $fref ) { // Have we passed the starting point?
echo "<TR><TD COLSPAN=2 ALIGN=center>";
echo "Ahem! $fref is not a Factorial! </TD></TR>";
echo "</TD></TR>";
$nn = 0;
} else if ( $ff == $fref ) {
echo "<TR><TD ALIGN=right> #$jj: </TD><TD> $ff </TD></TR>";
$fref = $ff*($jj+1); // Set $fref = next Factorial. (Crude, but it works.)
$nn--;
}
$jj++; // Increment index.
$ff = $ff*$jj; // Next factorial.
}
// -- All done. Wrap it up and close the HTML output:
echo "
</TABLE>
<P>
<A HREF=fact.php?fstart=0&nn=0>Try again</A> or
<A HREF=.>exit</A>?
</CENTER>
</BODY>
</HTML>
";
// -- Note: see "Page Source" in your browser's
// "View" menu to see the HTML this generates.
?>