2075 lines
39 KiB
HTML
2075 lines
39 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
<HTML
|
|
><HEAD
|
|
><TITLE
|
|
>Writing Scripts</TITLE
|
|
><META
|
|
NAME="GENERATOR"
|
|
CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK
|
|
REL="HOME"
|
|
TITLE="Advanced Bash-Scripting Guide"
|
|
HREF="index.html"><LINK
|
|
REL="UP"
|
|
TITLE="Exercises"
|
|
HREF="exercises.html"><LINK
|
|
REL="PREVIOUS"
|
|
TITLE="Analyzing Scripts"
|
|
HREF="scriptanalysis.html"><LINK
|
|
REL="NEXT"
|
|
TITLE="Revision History"
|
|
HREF="revisionhistory.html"></HEAD
|
|
><BODY
|
|
CLASS="SECT1"
|
|
BGCOLOR="#FFFFFF"
|
|
TEXT="#000000"
|
|
LINK="#0000FF"
|
|
VLINK="#840084"
|
|
ALINK="#0000FF"
|
|
><DIV
|
|
CLASS="NAVHEADER"
|
|
><TABLE
|
|
SUMMARY="Header navigation table"
|
|
WIDTH="100%"
|
|
BORDER="0"
|
|
CELLPADDING="0"
|
|
CELLSPACING="0"
|
|
><TR
|
|
><TH
|
|
COLSPAN="3"
|
|
ALIGN="center"
|
|
>Advanced Bash-Scripting Guide: </TH
|
|
></TR
|
|
><TR
|
|
><TD
|
|
WIDTH="10%"
|
|
ALIGN="left"
|
|
VALIGN="bottom"
|
|
><A
|
|
HREF="scriptanalysis.html"
|
|
ACCESSKEY="P"
|
|
>Prev</A
|
|
></TD
|
|
><TD
|
|
WIDTH="80%"
|
|
ALIGN="center"
|
|
VALIGN="bottom"
|
|
>Appendix O. Exercises</TD
|
|
><TD
|
|
WIDTH="10%"
|
|
ALIGN="right"
|
|
VALIGN="bottom"
|
|
><A
|
|
HREF="revisionhistory.html"
|
|
ACCESSKEY="N"
|
|
>Next</A
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
><HR
|
|
ALIGN="LEFT"
|
|
WIDTH="100%"></DIV
|
|
><DIV
|
|
CLASS="SECT1"
|
|
><H1
|
|
CLASS="SECT1"
|
|
><A
|
|
NAME="WRITINGSCRIPTS"
|
|
></A
|
|
>O.2. Writing Scripts</H1
|
|
><P
|
|
><A
|
|
NAME="WRITINGSCRIPTS1"
|
|
></A
|
|
></P
|
|
><P
|
|
>Write a script to carry out each of the following tasks.</P
|
|
><P
|
|
></P
|
|
><DIV
|
|
CLASS="VARIABLELIST"
|
|
><P
|
|
><B
|
|
><A
|
|
NAME="EXEASY1"
|
|
></A
|
|
>EASY</B
|
|
></P
|
|
><DL
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Self-reproducing Script</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Write a script that backs itself up, that is, copies
|
|
itself to a file named <TT
|
|
CLASS="FILENAME"
|
|
>backup.sh</TT
|
|
>.</P
|
|
><P
|
|
>Hint: Use the <A
|
|
HREF="basic.html#CATREF"
|
|
>cat</A
|
|
> command
|
|
and the appropriate <A
|
|
HREF="othertypesv.html#SCRNAMEPARAM"
|
|
>positional
|
|
parameter</A
|
|
>.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Home Directory Listing</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Perform a recursive directory listing on the user's home
|
|
directory and save the information to a file. Compress
|
|
the file, have the script prompt the user to insert
|
|
a USB flash drive, then press <B
|
|
CLASS="KEYCAP"
|
|
>ENTER</B
|
|
>.
|
|
Finally, save the file to the flash drive after making
|
|
certain the flash drive has properly mounted by parsing
|
|
the output of <A
|
|
HREF="system.html#DFREF"
|
|
>df</A
|
|
>. Note that
|
|
the flash drive must be <I
|
|
CLASS="FIRSTTERM"
|
|
>unmounted</I
|
|
>
|
|
before it is removed.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Converting <A
|
|
HREF="loops1.html#FORLOOPREF1"
|
|
>for</A
|
|
>
|
|
loops to <A
|
|
HREF="loops1.html#WHILELOOPREF"
|
|
>while</A
|
|
> and <A
|
|
HREF="loops1.html#UNTILLOOPREF"
|
|
>until</A
|
|
> loops</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Convert the <I
|
|
CLASS="FIRSTTERM"
|
|
>for loops</I
|
|
> in <A
|
|
HREF="loops1.html#EX22"
|
|
>Example 11-1</A
|
|
> to <I
|
|
CLASS="FIRSTTERM"
|
|
>while
|
|
loops</I
|
|
>. Hint: store the data in an <A
|
|
HREF="arrays.html#ARRAYREF"
|
|
>array</A
|
|
> and step through the array
|
|
elements.</P
|
|
><P
|
|
>Having already done the <SPAN
|
|
CLASS="QUOTE"
|
|
>"heavy lifting,"</SPAN
|
|
>
|
|
now convert the loops in the example to <I
|
|
CLASS="FIRSTTERM"
|
|
> until
|
|
loops</I
|
|
>.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Changing the line spacing of a text file</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Write a script that reads each line of a target file, then
|
|
writes the line back to <TT
|
|
CLASS="FILENAME"
|
|
>stdout</TT
|
|
>, but with
|
|
an extra blank line following. This has the effect of
|
|
<EM
|
|
>double-spacing</EM
|
|
> the file.</P
|
|
><P
|
|
>Include all necessary code to check whether the script
|
|
gets the necessary command-line argument (a filename),
|
|
and whether the specified file exists.</P
|
|
><P
|
|
>When the script runs correctly, modify it to
|
|
<EM
|
|
>triple-space</EM
|
|
> the target file.</P
|
|
><P
|
|
>Finally, write a script to remove all blank lines from
|
|
the target file, <EM
|
|
>single-spacing</EM
|
|
> it.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Backwards Listing</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Write a script that echoes itself to
|
|
<TT
|
|
CLASS="FILENAME"
|
|
>stdout</TT
|
|
>, but
|
|
<EM
|
|
>backwards</EM
|
|
>.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Automatically Decompressing Files</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Given a list of filenames as input, this script
|
|
queries each target file (parsing the output of the
|
|
<A
|
|
HREF="filearchiv.html#FILEREF"
|
|
>file</A
|
|
> command) for
|
|
the type of compression used on it. Then the script
|
|
automatically invokes the appropriate decompression command
|
|
(<B
|
|
CLASS="COMMAND"
|
|
>gunzip</B
|
|
>, <B
|
|
CLASS="COMMAND"
|
|
>bunzip2</B
|
|
>,
|
|
<B
|
|
CLASS="COMMAND"
|
|
>unzip</B
|
|
>, <B
|
|
CLASS="COMMAND"
|
|
>uncompress</B
|
|
>,
|
|
or whatever). If a target file is not compressed, the
|
|
script emits a warning message, but takes no other action
|
|
on that particular file.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Unique System ID</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Generate a <SPAN
|
|
CLASS="QUOTE"
|
|
>"unique"</SPAN
|
|
> 6-digit hexadecimal
|
|
identifier for your computer. Do <EM
|
|
>not</EM
|
|
>
|
|
use the flawed <A
|
|
HREF="system.html#HOSTIDREF"
|
|
>hostid</A
|
|
>
|
|
command. Hint: <B
|
|
CLASS="COMMAND"
|
|
><A
|
|
HREF="filearchiv.html#MD5SUMREF"
|
|
>md5sum</A
|
|
>
|
|
<A
|
|
HREF="files.html#DATAFILESREF1"
|
|
><TT
|
|
CLASS="FILENAME"
|
|
>/etc/passwd</TT
|
|
></A
|
|
></B
|
|
>,
|
|
then select the first 6 digits of output.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Backup</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Archive as a <SPAN
|
|
CLASS="QUOTE"
|
|
>"tarball"</SPAN
|
|
>
|
|
(<TT
|
|
CLASS="FILENAME"
|
|
>*.tar.gz</TT
|
|
> file) all the files
|
|
in your home directory tree
|
|
(<TT
|
|
CLASS="FILENAME"
|
|
>/home/your-name</TT
|
|
>) that have
|
|
been modified in the last 24 hours. Hint: use <A
|
|
HREF="moreadv.html#FINDREF"
|
|
>find</A
|
|
>.</P
|
|
><P
|
|
>Optional: you may use this as the basis of a
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>backup</I
|
|
> script.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Checking whether a process is still running</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Given a <A
|
|
HREF="special-chars.html#PROCESSIDREF"
|
|
>process ID</A
|
|
>
|
|
(<I
|
|
CLASS="FIRSTTERM"
|
|
>PID</I
|
|
>) as an argument, this script
|
|
will check, at user-specified intervals, whether
|
|
the given process is still running. You may use
|
|
the <A
|
|
HREF="system.html#PPSSREF"
|
|
>ps</A
|
|
> and <A
|
|
HREF="timedate.html#SLEEPREF"
|
|
>sleep</A
|
|
> commands.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Primes</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Print (to <TT
|
|
CLASS="FILENAME"
|
|
>stdout</TT
|
|
>) all
|
|
prime numbers between 60000 and 63000. The output
|
|
should be nicely formatted in columns (hint:
|
|
use <A
|
|
HREF="internal.html#PRINTFREF"
|
|
>printf</A
|
|
>).</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Lottery Numbers</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>One type of lottery involves picking five
|
|
different numbers, in the range of 1 - 50. Write a
|
|
script that generates five pseudorandom numbers in this
|
|
range, <EM
|
|
>with no duplicates</EM
|
|
>. The
|
|
script will give the option of echoing the numbers to
|
|
<TT
|
|
CLASS="FILENAME"
|
|
>stdout</TT
|
|
> or saving them to a file,
|
|
along with the date and time the particular number set
|
|
was generated. (If your script consistently generates
|
|
<EM
|
|
>winning</EM
|
|
> lottery numbers, then you
|
|
can retire on the proceeds and leave shell scripting to
|
|
those of us who have to work for a living.)</P
|
|
></DD
|
|
></DL
|
|
></DIV
|
|
><P
|
|
></P
|
|
><DIV
|
|
CLASS="VARIABLELIST"
|
|
><P
|
|
><B
|
|
><A
|
|
NAME="EXMEDIUM1"
|
|
></A
|
|
>INTERMEDIATE</B
|
|
></P
|
|
><DL
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Integer or String</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Write a script <A
|
|
HREF="functions.html#FUNCTIONREF"
|
|
>function</A
|
|
>
|
|
that determines if an argument passed to it is an integer
|
|
or a string. The function will return TRUE (0) if
|
|
passed an integer, and FALSE (1) if passed a string.</P
|
|
><P
|
|
>Hint: What does the following expression return
|
|
when <TT
|
|
CLASS="VARNAME"
|
|
>$1</TT
|
|
> is <EM
|
|
>not</EM
|
|
>
|
|
an integer?</P
|
|
><P
|
|
><TT
|
|
CLASS="VARNAME"
|
|
>expr $1 + 0</TT
|
|
></P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
><A
|
|
HREF="special-chars.html#ASCIIDEF"
|
|
>ASCII</A
|
|
>
|
|
to Integer</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>The <I
|
|
CLASS="FIRSTTERM"
|
|
>atoi</I
|
|
> function in
|
|
<B
|
|
CLASS="COMMAND"
|
|
>C</B
|
|
> converts a string character to
|
|
an integer. Write a shell script function that performs
|
|
the same operation. Likewise, write a shell script function
|
|
that does the inverse, mirroring the <B
|
|
CLASS="COMMAND"
|
|
>C</B
|
|
>
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>itoa</I
|
|
> function which converts an
|
|
integer into an ASCII character.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Managing Disk Space</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>List, one at a time, all files larger than 100K in
|
|
the <TT
|
|
CLASS="FILENAME"
|
|
>/home/username</TT
|
|
>
|
|
directory tree. Give the user the option to delete or
|
|
compress the file, then proceed to show the next one. Write
|
|
to a logfile the names of all deleted files and the
|
|
deletion times.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Banner</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Simulate the functionality of the deprecated <A
|
|
HREF="extmisc.html#BANNERREF"
|
|
>banner</A
|
|
> command in a script.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Removing Inactive Accounts</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Inactive accounts on a network server waste disk space and may
|
|
become a security risk. Write an administrative script
|
|
(to be invoked by <I
|
|
CLASS="FIRSTTERM"
|
|
>root</I
|
|
> or the <A
|
|
HREF="system.html#CRONREF"
|
|
>cron daemon</A
|
|
>) that checks
|
|
for and deletes user accounts that have not been accessed
|
|
within the last 90 days.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Enforcing Disk Quotas</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Write a script for a multi-user system that checks users'
|
|
disk usage. If a user surpasses a preset limit
|
|
(500 MB, for example) in her <TT
|
|
CLASS="FILENAME"
|
|
>/home/username</TT
|
|
>
|
|
directory, then the script automatically sends her a
|
|
<SPAN
|
|
CLASS="QUOTE"
|
|
>"pigout"</SPAN
|
|
> warning e-mail.</P
|
|
><P
|
|
>The
|
|
script will use the <A
|
|
HREF="system.html#DUREF"
|
|
>du</A
|
|
>
|
|
and <A
|
|
HREF="communications.html#COMMMAIL1"
|
|
>mail</A
|
|
> commands. As
|
|
an option, it will allow setting and enforcing quotas
|
|
using the <A
|
|
HREF="system.html#QUOTAREF"
|
|
>quota</A
|
|
> and <A
|
|
HREF="system.html#SETQUOTAREF"
|
|
>setquota</A
|
|
> commands.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Logged in User Information</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>For all logged in users, show their real names and the time
|
|
and date of their last login.</P
|
|
><P
|
|
>Hint: use <A
|
|
HREF="system.html#WHOREF"
|
|
>who</A
|
|
>,
|
|
<A
|
|
HREF="system.html#LASTLOGREF"
|
|
>lastlog</A
|
|
>,
|
|
and parse <A
|
|
HREF="files.html#DATAFILESREF1"
|
|
><TT
|
|
CLASS="FILENAME"
|
|
>/etc/passwd</TT
|
|
></A
|
|
>.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Safe Delete</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Implement, as a script, a <SPAN
|
|
CLASS="QUOTE"
|
|
>"safe"</SPAN
|
|
> delete
|
|
command, <TT
|
|
CLASS="FILENAME"
|
|
>sdel.sh</TT
|
|
>. Filenames passed as
|
|
command-line arguments to this script are not deleted,
|
|
but instead <A
|
|
HREF="filearchiv.html#GZIPREF"
|
|
>gzipped</A
|
|
>
|
|
if not already compressed (use <A
|
|
HREF="filearchiv.html#FILEREF"
|
|
>file</A
|
|
> to check), then moved
|
|
to a <TT
|
|
CLASS="FILENAME"
|
|
>~/TRASH</TT
|
|
>
|
|
directory. Upon invocation, the script checks the <TT
|
|
CLASS="FILENAME"
|
|
>~/TRASH</TT
|
|
> directory for files
|
|
older than 48 hours and <A
|
|
HREF="basic.html#RMREF"
|
|
>permanently
|
|
deletes</A
|
|
> them. (An better alternative might be to
|
|
have a second script handle this, periodically invoked
|
|
by the <A
|
|
HREF="system.html#CRONREF"
|
|
>cron daemon</A
|
|
>.)</P
|
|
><P
|
|
><EM
|
|
>Extra credit:</EM
|
|
> Write the script
|
|
so it can handle files and directories <A
|
|
HREF="basic.html#RMRECURS"
|
|
>recursively</A
|
|
>. This would give it
|
|
the capability of <SPAN
|
|
CLASS="QUOTE"
|
|
>"safely deleting"</SPAN
|
|
> entire
|
|
directory structures.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Making Change</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>What is the most efficient way to make change for $1.68,
|
|
using only coins in common circulations (up to 25c)? It's
|
|
6 quarters, 1 dime, a nickel, and three cents.</P
|
|
><P
|
|
>Given any arbitrary command-line input in dollars and
|
|
cents ($*.??), calculate the change, using the minimum
|
|
number of coins. If your home country is not the United
|
|
States, you may use your local currency units instead. The
|
|
script will need to parse the command-line input, then
|
|
change it to multiples of the smallest monetary unit (cents
|
|
or whatever). Hint: look at <A
|
|
HREF="complexfunct.html#EX61"
|
|
>Example 24-8</A
|
|
>.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Quadratic Equations</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Solve a <I
|
|
CLASS="FIRSTTERM"
|
|
>quadratic</I
|
|
> equation of the form
|
|
<TT
|
|
CLASS="PARAMETER"
|
|
><I
|
|
>Ax^2 + Bx + C = 0</I
|
|
></TT
|
|
>. Have a script take
|
|
as arguments the coefficients, <TT
|
|
CLASS="USERINPUT"
|
|
><B
|
|
>A</B
|
|
></TT
|
|
>,
|
|
<TT
|
|
CLASS="USERINPUT"
|
|
><B
|
|
>B</B
|
|
></TT
|
|
>, and <TT
|
|
CLASS="USERINPUT"
|
|
><B
|
|
>C</B
|
|
></TT
|
|
>,
|
|
and return the solutions to five decimal places.</P
|
|
><P
|
|
>Hint: pipe the coefficients to <A
|
|
HREF="mathc.html#BCREF"
|
|
>bc</A
|
|
>, using the well-known formula,
|
|
<TT
|
|
CLASS="PARAMETER"
|
|
><I
|
|
>x = ( -B +/- sqrt( B^2 - 4AC ) ) / 2A</I
|
|
></TT
|
|
>.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Table of Logarithms</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Using the <A
|
|
HREF="mathc.html#BCREF"
|
|
>bc</A
|
|
> and <A
|
|
HREF="internal.html#PRINTFREF"
|
|
>printf</A
|
|
> commands, print out a
|
|
nicely-formatted table of eight-place natural logarithms
|
|
in the interval between 0.00 and 100.00, in steps of
|
|
.01.</P
|
|
><P
|
|
>Hint: <I
|
|
CLASS="FIRSTTERM"
|
|
>bc</I
|
|
> requires the
|
|
<TT
|
|
CLASS="OPTION"
|
|
>-l</TT
|
|
> option to load the math library.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Unicode Table</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Using <A
|
|
HREF="asciitable.html#ASCIISH"
|
|
>Example T-1</A
|
|
> as a template,
|
|
write a script that prints to a file a complete
|
|
<A
|
|
HREF="bashver4.html#UNICODEREF"
|
|
>Unicode</A
|
|
> table.</P
|
|
><P
|
|
>Hint: Use the <TT
|
|
CLASS="OPTION"
|
|
>-e</TT
|
|
> option to
|
|
<A
|
|
HREF="internal.html#ECHOREF"
|
|
>echo</A
|
|
>:
|
|
<B
|
|
CLASS="COMMAND"
|
|
>echo -e '\uXXXX'</B
|
|
>, where
|
|
<TT
|
|
CLASS="REPLACEABLE"
|
|
><I
|
|
>XXXX</I
|
|
></TT
|
|
>
|
|
is the Unicode numerical character designation.
|
|
This requires <A
|
|
HREF="bashver4.html#BASH42"
|
|
>version 4.2</A
|
|
>
|
|
or later of Bash.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Sum of Matching Numbers</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Find the sum of all five-digit numbers (in the range
|
|
10000 - 99999) containing <EM
|
|
>exactly two</EM
|
|
>
|
|
out of the following set of digits: { 4, 5, 6 }. These may
|
|
repeat within the same number, and if so, they count once
|
|
for each occurrence.</P
|
|
><P
|
|
>Some examples of <I
|
|
CLASS="FIRSTTERM"
|
|
>matching numbers</I
|
|
> are
|
|
42057, 74638, and 89515.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Lucky Numbers</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>A <I
|
|
CLASS="FIRSTTERM"
|
|
>lucky number</I
|
|
> is one whose
|
|
individual digits add up to 7, in successive additions. For
|
|
example, 62431 is a <I
|
|
CLASS="FIRSTTERM"
|
|
>lucky number</I
|
|
>
|
|
(6 + 2 + 4 + 3 + 1 = 16, 1 + 6 = 7). Find all the
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>lucky numbers</I
|
|
> between 1000 and
|
|
10000.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Craps</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Borrowing the ASCII graphics from <A
|
|
HREF="contributed-scripts.html#PETALS"
|
|
>Example A-40</A
|
|
>,
|
|
write a script that plays the well-known gambling game of
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>craps</I
|
|
>. The script will accept bets
|
|
from one or more players, roll the dice, and keep track of
|
|
wins and losses, as well as of each player's bankroll.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Tic-tac-toe</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Write a script that plays the child's game of
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>tic-tac-toe</I
|
|
> against a human
|
|
player. The script will let the human choose whether
|
|
to take the first move. The script will follow
|
|
an optimal strategy, and therefore never lose. To simplify
|
|
matters, you may use ASCII graphics:</P
|
|
><P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="90%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
> o | x |
|
|
----------
|
|
| x |
|
|
----------
|
|
| o |
|
|
|
|
Your move, human (row, column)?</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Alphabetizing a String</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Alphabetize (in ASCII order) an arbitrary string
|
|
read from the command-line.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Parsing</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Parse <A
|
|
HREF="files.html#DATAFILESREF1"
|
|
><TT
|
|
CLASS="FILENAME"
|
|
>/etc/passwd</TT
|
|
></A
|
|
>,
|
|
and output its contents in nice, easy-to-read tabular
|
|
form.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Logging Logins</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Parse <TT
|
|
CLASS="FILENAME"
|
|
>/var/log/messages</TT
|
|
> to
|
|
produce a nicely formatted file of user logins and login
|
|
times. The script may need to run as
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>root</I
|
|
>. (Hint: Search for the string
|
|
<SPAN
|
|
CLASS="QUOTE"
|
|
>"LOGIN."</SPAN
|
|
>)</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Pretty-Printing a Data File</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Certain database and spreadsheet packages use
|
|
save-files with the fields separated by commas, commonly
|
|
referred to as <I
|
|
CLASS="FIRSTTERM"
|
|
>comma-separated values</I
|
|
>
|
|
or CSVs. Other applications often need to parse these
|
|
files.</P
|
|
><P
|
|
>Given a data file with comma-separated
|
|
<A
|
|
HREF="special-chars.html#FIELDREF"
|
|
>fields</A
|
|
>, of the form:
|
|
<TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="90%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>Jones,Bill,235 S. Williams St.,Denver,CO,80221,(303) 244-7989
|
|
Smith,Tom,404 Polk Ave.,Los Angeles,CA,90003,(213) 879-5612
|
|
...</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
>
|
|
Reformat the data and print it out to
|
|
<TT
|
|
CLASS="FILENAME"
|
|
>stdout</TT
|
|
> in labeled, evenly-spaced columns.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Justification</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Given ASCII text input either from
|
|
<TT
|
|
CLASS="FILENAME"
|
|
>stdin</TT
|
|
> or a file, adjust
|
|
the word spacing to right-justify each line to a
|
|
user-specified line-width, then send the output to
|
|
<TT
|
|
CLASS="FILENAME"
|
|
>stdout</TT
|
|
>.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Mailing List</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Using the <A
|
|
HREF="communications.html#COMMMAIL1"
|
|
>mail</A
|
|
> command,
|
|
write a script that manages a simple mailing list. The
|
|
script automatically e-mails the monthly company newsletter,
|
|
read from a specified text file, and sends it to all the
|
|
addresses on the mailing list, which the script reads from
|
|
another specified file.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Generating Passwords</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Generate pseudorandom 8-character passwords, using
|
|
characters in the ranges [0-9], [A-Z], [a-z]. Each password
|
|
must contain at least two digits.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Monitoring a User</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>You suspect that one particular user on the network
|
|
has been abusing her privileges and possibly attempting to
|
|
hack the system. Write a script to automatically monitor
|
|
and log her activities when she's signed on. The log file
|
|
will save entries for the previous week, and delete those
|
|
entries more than seven days old.</P
|
|
><P
|
|
>You may use <A
|
|
HREF="system.html#LASTREF"
|
|
>last</A
|
|
>,
|
|
<A
|
|
HREF="system.html#LASTLOGREF"
|
|
>lastlog</A
|
|
>, and <A
|
|
HREF="system.html#LASTCOMMREF"
|
|
>lastcomm</A
|
|
> to aid your
|
|
surveillance of the suspected fiend.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Checking for Broken Links</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Using <A
|
|
HREF="communications.html#LYNXREF"
|
|
>lynx</A
|
|
> with the
|
|
<TT
|
|
CLASS="OPTION"
|
|
>-traversal</TT
|
|
> option, write a script that
|
|
checks a Web site for broken links.</P
|
|
></DD
|
|
></DL
|
|
></DIV
|
|
><P
|
|
></P
|
|
><DIV
|
|
CLASS="VARIABLELIST"
|
|
><P
|
|
><B
|
|
><A
|
|
NAME="EXDIFFICULT1"
|
|
></A
|
|
>DIFFICULT</B
|
|
></P
|
|
><DL
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Testing Passwords</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Write a script to check and validate passwords. The object
|
|
is to flag <SPAN
|
|
CLASS="QUOTE"
|
|
>"weak"</SPAN
|
|
> or easily guessed password
|
|
candidates.</P
|
|
><P
|
|
>A trial password will be input to the script as a
|
|
command-line parameter. To be considered acceptable,
|
|
a password must meet the following minimum qualifications:
|
|
|
|
<P
|
|
></P
|
|
><UL
|
|
><LI
|
|
><P
|
|
>Minimum length of 8 characters</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
>Must contain at least one numeric character</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
>Must contain at least one of the following
|
|
non-alphabetic characters: <SPAN
|
|
CLASS="TOKEN"
|
|
>@</SPAN
|
|
>,
|
|
<SPAN
|
|
CLASS="TOKEN"
|
|
>#</SPAN
|
|
>, <SPAN
|
|
CLASS="TOKEN"
|
|
>$</SPAN
|
|
>, <SPAN
|
|
CLASS="TOKEN"
|
|
>%</SPAN
|
|
>,
|
|
<SPAN
|
|
CLASS="TOKEN"
|
|
>&</SPAN
|
|
>, <SPAN
|
|
CLASS="TOKEN"
|
|
>*</SPAN
|
|
>, <SPAN
|
|
CLASS="TOKEN"
|
|
>+</SPAN
|
|
>,
|
|
<SPAN
|
|
CLASS="TOKEN"
|
|
>-</SPAN
|
|
>, <SPAN
|
|
CLASS="TOKEN"
|
|
>=</SPAN
|
|
></P
|
|
></LI
|
|
></UL
|
|
></P
|
|
><P
|
|
>Optional:
|
|
|
|
<P
|
|
></P
|
|
><UL
|
|
><LI
|
|
><P
|
|
>Do a dictionary check on every sequence of at least
|
|
four consecutive alphabetic characters in the password under
|
|
test. This will eliminate passwords containing embedded
|
|
<SPAN
|
|
CLASS="QUOTE"
|
|
>"words"</SPAN
|
|
> found in a standard dictionary.</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
>Enable the script to check all the passwords on your
|
|
system. These do not reside in
|
|
<A
|
|
HREF="files.html#DATAFILESREF1"
|
|
><TT
|
|
CLASS="FILENAME"
|
|
>/etc/passwd</TT
|
|
></A
|
|
>.</P
|
|
></LI
|
|
></UL
|
|
></P
|
|
><P
|
|
>This exercise tests mastery of <A
|
|
HREF="regexp.html#REGEXREF"
|
|
>Regular Expressions</A
|
|
>.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Cross Reference</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Write a script that generates a
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>cross-reference</I
|
|
>
|
|
(<I
|
|
CLASS="FIRSTTERM"
|
|
>concordance</I
|
|
>) on a target file.
|
|
The output will be a listing of all word occurrences in
|
|
the target file, along with the line numbers in which
|
|
each word occurs. Traditionally, <I
|
|
CLASS="FIRSTTERM"
|
|
>linked
|
|
list</I
|
|
> constructs would be used in such
|
|
applications. Therefore, you should investigate <A
|
|
HREF="arrays.html#ARRAYREF"
|
|
>arrays</A
|
|
> in the course of
|
|
this exercise. <A
|
|
HREF="textproc.html#WF"
|
|
>Example 16-12</A
|
|
> is probably
|
|
<EM
|
|
>not</EM
|
|
> a good place to start.</P
|
|
></DD
|
|
><DT
|
|
><A
|
|
NAME="NEWTONSQRT"
|
|
></A
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Square Root</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Write a script to calculate square roots of numbers
|
|
using <I
|
|
CLASS="FIRSTTERM"
|
|
>Newton's Method</I
|
|
>.</P
|
|
><P
|
|
>The algorithm for this, expressed as a snippet of Bash
|
|
<A
|
|
HREF="assortedtips.html#PSEUDOCODEREF"
|
|
>pseudo-code</A
|
|
> is:</P
|
|
><P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="90%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
># (Isaac) Newton's Method for speedy extraction
|
|
#+ of square roots.
|
|
|
|
guess = $argument
|
|
# $argument is the number to find the square root of.
|
|
# $guess is each successive calculated "guess" -- or trial solution --
|
|
#+ of the square root.
|
|
# Our first "guess" at a square root is the argument itself.
|
|
|
|
oldguess = 0
|
|
# $oldguess is the previous $guess.
|
|
|
|
tolerance = .000001
|
|
# To how close a tolerance we wish to calculate.
|
|
|
|
loopcnt = 0
|
|
# Let's keep track of how many times through the loop.
|
|
# Some arguments will require more loop iterations than others.
|
|
|
|
|
|
while [ ABS( $guess $oldguess ) -gt $tolerance ]
|
|
# ^^^^^^^^^^^^^^^^^^^^^^^ Fix up syntax, of course.
|
|
|
|
# "ABS" is a (floating point) function to find the absolute value
|
|
#+ of the difference between the two terms.
|
|
# So, as long as difference between current and previous
|
|
#+ trial solution (guess) exceeds the tolerance, keep looping.
|
|
|
|
do
|
|
oldguess = $guess # Update $oldguess to previous $guess.
|
|
|
|
# =======================================================
|
|
guess = ( $oldguess + ( $argument / $oldguess ) ) / 2.0
|
|
# = 1/2 ( ($oldguess **2 + $argument) / $oldguess )
|
|
# equivalent to:
|
|
# = 1/2 ( $oldguess + $argument / $oldguess )
|
|
# that is, "averaging out" the trial solution and
|
|
#+ the proportion of argument deviation
|
|
#+ (in effect, splitting the error in half).
|
|
# This converges on an accurate solution
|
|
#+ with surprisingly few loop iterations . . .
|
|
#+ for arguments > $tolerance, of course.
|
|
# =======================================================
|
|
|
|
(( loopcnt++ )) # Update loop counter.
|
|
done</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></P
|
|
><P
|
|
>It's a simple enough recipe, and
|
|
<EM
|
|
>seems</EM
|
|
> at first glance easy enough to
|
|
convert into a working Bash script. The problem, though,
|
|
is that Bash has <A
|
|
HREF="ops.html#NOFLOATINGPOINT"
|
|
>no native
|
|
support for floating point numbers</A
|
|
>. So, the script
|
|
writer needs to use <A
|
|
HREF="mathc.html#BCREF"
|
|
>bc</A
|
|
> or
|
|
possibly <A
|
|
HREF="awk.html#AWKREF"
|
|
>awk</A
|
|
> to convert the
|
|
numbers and do the calculations. It could get rather messy
|
|
. . .</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Logging File Accesses</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Log all accesses to the files in <TT
|
|
CLASS="FILENAME"
|
|
>/etc</TT
|
|
> during the course of
|
|
a single day. This information should include the filename,
|
|
user name, and access time. If any alterations to the
|
|
files take place, that will be flagged. Write this data
|
|
as tabular (tab-separated) formatted records in a logfile.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Monitoring Processes</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Write a script to continually monitor all running
|
|
processes and to keep track of how many child processes each
|
|
parent spawns. If a process spawns more than five children,
|
|
then the script sends an e-mail to the system administrator
|
|
(or <I
|
|
CLASS="FIRSTTERM"
|
|
>root</I
|
|
>) with all relevant
|
|
information, including the time, PID of the parent, PIDs
|
|
of the children, etc. The script appends a report to a log
|
|
file every ten minutes. </P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Strip Comments</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Strip all comments from a shell script whose name
|
|
is specified on the command-line. Note that the initial
|
|
<A
|
|
HREF="sha-bang.html#SHABANGREF"
|
|
>#! line</A
|
|
> must not be
|
|
stripped out.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Strip HTML Tags</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Strip all the HTML tags from a specified HTML file, then
|
|
reformat it into lines between 60 and 75 characters
|
|
in length. Reset paragraph and block spacing, as
|
|
appropriate, and convert HTML tables to their approximate
|
|
text equivalent.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>XML Conversion</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Convert an XML file to both HTML and text format.</P
|
|
><P
|
|
>Optional: A script that converts Docbook/SGML to XML.</P
|
|
></DD
|
|
><DT
|
|
><A
|
|
NAME="CSPAMMERS"
|
|
></A
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Chasing Spammers</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
> Write a script that analyzes a spam e-mail by doing
|
|
DNS lookups on the IP addresses in the headers to identify
|
|
the relay hosts as well as the originating ISP. The
|
|
script will forward the unaltered spam message to the
|
|
responsible ISPs. Of course, it will be necessary to
|
|
filter out <EM
|
|
>your own ISP's IP address</EM
|
|
>,
|
|
so you don't end up complaining about yourself.</P
|
|
><P
|
|
>As necessary, use the appropriate <A
|
|
HREF="communications.html#COMMUNINFO1"
|
|
>network analysis commands</A
|
|
>.</P
|
|
><P
|
|
>For some ideas, see <A
|
|
HREF="communications.html#ISSPAMMER"
|
|
>Example 16-41</A
|
|
> and <A
|
|
HREF="contributed-scripts.html#ISSPAMMER2"
|
|
>Example A-28</A
|
|
>.</P
|
|
><P
|
|
>Optional: Write a script that searches through a list of
|
|
e-mail messages and deletes the spam according to specified
|
|
filters.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Creating man pages</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Write a script that automates the process of creating
|
|
<A
|
|
HREF="basic.html#MANREF"
|
|
>man pages</A
|
|
>.</P
|
|
><P
|
|
>Given a text file which contains information to be
|
|
formatted into a <I
|
|
CLASS="FIRSTTERM"
|
|
>man page</I
|
|
>, the
|
|
script will read the file, then invoke the appropriate
|
|
<A
|
|
HREF="textproc.html#GROFFREF"
|
|
>groff</A
|
|
> commands to
|
|
output the corresponding <I
|
|
CLASS="FIRSTTERM"
|
|
>man page</I
|
|
>
|
|
to <TT
|
|
CLASS="FILENAME"
|
|
>stdout</TT
|
|
>. The text file contains
|
|
blocks of information under the standard <I
|
|
CLASS="FIRSTTERM"
|
|
>man
|
|
page</I
|
|
> headings, i.e., NAME, SYNOPSIS,
|
|
DESCRIPTION, etc.</P
|
|
><P
|
|
><A
|
|
HREF="contributed-scripts.html#MANED"
|
|
>Example A-39</A
|
|
> is an instructive first step.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Hex Dump</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Do a hex(adecimal) dump on a binary file
|
|
specified as an argument to the script. The output should
|
|
be in neat tabular <A
|
|
HREF="special-chars.html#FIELDREF"
|
|
>fields</A
|
|
>,
|
|
with the first field showing the address, each of the
|
|
next 8 fields a 4-byte hex number, and the final field
|
|
the ASCII equivalent of the previous 8 fields.</P
|
|
><P
|
|
>The obvious followup to this is to extend the hex
|
|
dump script into a disassembler. Using a lookup table,
|
|
or some other clever gimmick, convert the hex values into
|
|
80x86 op codes.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Emulating a Shift Register</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Using <A
|
|
HREF="arrays.html#STACKEX"
|
|
>Example 27-15</A
|
|
> as an inspiration,
|
|
write a script that emulates a 64-bit shift register as
|
|
an <A
|
|
HREF="arrays.html#ARRAYREF"
|
|
>array</A
|
|
>. Implement
|
|
functions to <I
|
|
CLASS="FIRSTTERM"
|
|
>load</I
|
|
> the register,
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>shift left</I
|
|
>, <I
|
|
CLASS="FIRSTTERM"
|
|
>shift
|
|
right</I
|
|
>, and <I
|
|
CLASS="FIRSTTERM"
|
|
>rotate</I
|
|
>
|
|
it. Finally, write a function that interprets the register
|
|
contents as eight 8-bit ASCII characters.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Calculating Determinants</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Write a script that calculates
|
|
determinants
|
|
<A
|
|
NAME="AEN25254"
|
|
HREF="#FTN.AEN25254"
|
|
><SPAN
|
|
CLASS="footnote"
|
|
>[1]</SPAN
|
|
></A
|
|
>
|
|
|
|
by <A
|
|
HREF="localvar.html#RECURSIONREF0"
|
|
>recursively</A
|
|
> expanding the
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>minors</I
|
|
>. Use a 4 x 4 determinant as
|
|
a test case.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Hidden Words</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Write a <SPAN
|
|
CLASS="QUOTE"
|
|
>"word-find"</SPAN
|
|
> puzzle generator,
|
|
a script that hides 10 input words in a 10 x 10 array
|
|
of random letters. The words may be hidden across, down,
|
|
or diagonally.</P
|
|
><P
|
|
>Optional: Write a script that <EM
|
|
>solves</EM
|
|
>
|
|
word-find puzzles. To keep this from becoming too difficult,
|
|
the solution script will find only horizontal and vertical
|
|
words. (Hint: Treat each row and column as a string, and
|
|
search for substrings.)</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Anagramming</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
> Anagram 4-letter input. For example, the
|
|
anagrams of <EM
|
|
>word</EM
|
|
> are:
|
|
<EM
|
|
>do or rod row word</EM
|
|
>. You may use
|
|
<TT
|
|
CLASS="FILENAME"
|
|
>/usr/share/dict/linux.words</TT
|
|
> as the
|
|
reference list.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Word Ladders</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>A <SPAN
|
|
CLASS="QUOTE"
|
|
>"word ladder"</SPAN
|
|
> is a sequence of words,
|
|
with each successive word in the sequence differing from
|
|
the previous one by a single letter.</P
|
|
><P
|
|
>For example, to <SPAN
|
|
CLASS="QUOTE"
|
|
>"ladder"</SPAN
|
|
> from
|
|
<EM
|
|
>mark</EM
|
|
> to
|
|
<EM
|
|
>vase</EM
|
|
>:</P
|
|
><P
|
|
> <TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="90%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>mark --> park --> part --> past --> vast --> vase
|
|
^ ^ ^ ^ ^</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
>
|
|
</P
|
|
><P
|
|
>Write a script that solves word ladder puzzles. Given
|
|
a starting and an ending word, the script will list all
|
|
intermediate steps in the <SPAN
|
|
CLASS="QUOTE"
|
|
>"ladder."</SPAN
|
|
> Note
|
|
that <EM
|
|
>all</EM
|
|
> words in the sequence must
|
|
be legitimate dictionary words.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Fog Index</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>The <SPAN
|
|
CLASS="QUOTE"
|
|
>"fog index"</SPAN
|
|
> of a passage of text
|
|
estimates its reading difficulty, as a number corresponding
|
|
roughly to a school grade level. For example, a passage
|
|
with a fog index of 12 should be comprehensible to anyone
|
|
with 12 years of schooling.</P
|
|
><P
|
|
>The Gunning version of the fog index uses the following
|
|
algorithm.</P
|
|
><P
|
|
></P
|
|
><OL
|
|
TYPE="1"
|
|
><LI
|
|
><P
|
|
>Choose a section of the text at least
|
|
100 words in length.</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
>Count the number of sentences (a portion of
|
|
a sentence truncated by the boundary of the text section
|
|
counts as one).</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
>Find the average number of words per
|
|
sentence.</P
|
|
><P
|
|
>AVE_WDS_SEN = TOTAL_WORDS / SENTENCES</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
>Count the number of <SPAN
|
|
CLASS="QUOTE"
|
|
>"difficult"</SPAN
|
|
>
|
|
words in the segment -- those containing at least
|
|
3 syllables. Divide this quantity by total words to
|
|
get the proportion of difficult words.</P
|
|
><P
|
|
>PRO_DIFF_WORDS = LONG_WORDS / TOTAL_WORDS</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
>The Gunning fog index is the sum of the above two
|
|
quantities, multiplied by 0.4, then rounded to the
|
|
nearest integer.</P
|
|
><P
|
|
>G_FOG_INDEX = int ( 0.4 * ( AVE_WDS_SEN + PRO_DIFF_WORDS ) )</P
|
|
></LI
|
|
></OL
|
|
><P
|
|
>Step 4 is by far the most difficult portion of the
|
|
exercise. There exist various algorithms for estimating
|
|
the syllable count of a word. A rule-of-thumb formula
|
|
might consider the number of letters in a word and the
|
|
vowel-consonant mix.</P
|
|
><P
|
|
>A strict interpretation of the Gunning fog index does
|
|
not count compound words and proper nouns as
|
|
<SPAN
|
|
CLASS="QUOTE"
|
|
>"difficult"</SPAN
|
|
> words, but this would enormously
|
|
complicate the script.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Calculating PI using Buffon's Needle</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>The Eighteenth Century French mathematician de Buffon
|
|
came up with a novel experiment. Repeatedly drop a needle
|
|
of length <TT
|
|
CLASS="REPLACEABLE"
|
|
><I
|
|
>n</I
|
|
></TT
|
|
> onto a wooden floor
|
|
composed of long and narrow parallel boards. The cracks
|
|
separating the equal-width floorboards are a fixed distance
|
|
<TT
|
|
CLASS="REPLACEABLE"
|
|
><I
|
|
>d</I
|
|
></TT
|
|
> apart. Keep track of the
|
|
total drops and the number of times the needle intersects
|
|
a crack on the floor. The ratio of these two quantities
|
|
turns out to be a fractional multiple of PI.</P
|
|
><P
|
|
>In the spirit of <A
|
|
HREF="mathc.html#CANNON"
|
|
>Example 16-50</A
|
|
>, write a
|
|
script that runs a Monte Carlo simulation of
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>Buffon's Needle</I
|
|
>. To simplify matters,
|
|
set the needle length equal to the distance between the
|
|
cracks, <TT
|
|
CLASS="PARAMETER"
|
|
><I
|
|
>n = d</I
|
|
></TT
|
|
>.</P
|
|
><P
|
|
>Hint: there are actually two critical variables:
|
|
the distance from the center of the needle to the nearest
|
|
crack, and the inclination angle of the needle to that crack.
|
|
You may use <A
|
|
HREF="mathc.html#BCREF"
|
|
>bc</A
|
|
> to handle
|
|
the calculations.</P
|
|
></DD
|
|
><DT
|
|
><B
|
|
CLASS="COMMAND"
|
|
>Playfair Cipher</B
|
|
></DT
|
|
><DD
|
|
><P
|
|
>Implement the Playfair (Wheatstone) Cipher in a
|
|
script.</P
|
|
><P
|
|
>The Playfair Cipher encrypts text by substitution
|
|
of <I
|
|
CLASS="FIRSTTERM"
|
|
>digrams</I
|
|
> (2-letter groupings).
|
|
It is traditional to use a 5 x 5 letter scrambled-alphabet
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>key square</I
|
|
> for the encryption and
|
|
decryption.</P
|
|
><P
|
|
> <TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="90%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
> C O D E S
|
|
A B F G H
|
|
I K L M N
|
|
P Q R T U
|
|
V W X Y Z
|
|
|
|
Each letter of the alphabet appears once, except "I" also represents
|
|
"J". The arbitrarily chosen key word, "CODES" comes first, then all
|
|
the rest of the alphabet, in order from left to right, skipping letters
|
|
already used.
|
|
|
|
To encrypt, separate the plaintext message into digrams (2-letter
|
|
groups). If a group has two identical letters, delete the second, and
|
|
form a new group. If there is a single letter left over at the end,
|
|
insert a "null" character, typically an "X."
|
|
|
|
THIS IS A TOP SECRET MESSAGE
|
|
|
|
TH IS IS AT OP SE CR ET ME SA GE
|
|
|
|
|
|
|
|
For each digram, there are three possibilities.
|
|
-----------------------------------------------
|
|
|
|
1) Both letters will be on the same row of the key square:
|
|
For each letter, substitute the one immediately to the right, in that
|
|
row. If necessary, wrap around left to the beginning of the row.
|
|
|
|
or
|
|
|
|
2) Both letters will be in the same column of the key square:
|
|
For each letter, substitute the one immediately below it, in that
|
|
row. If necessary, wrap around to the top of the column.
|
|
|
|
or
|
|
|
|
3) Both letters will form the corners of a rectangle within the key square:
|
|
For each letter, substitute the one on the other corner the rectangle
|
|
which lies on the same row.
|
|
|
|
|
|
The "TH" digram falls under case #3.
|
|
G H
|
|
M N
|
|
T U (Rectangle with "T" and "H" at corners)
|
|
|
|
T --> U
|
|
H --> G
|
|
|
|
|
|
The "SE" digram falls under case #1.
|
|
C O D E S (Row containing "S" and "E")
|
|
|
|
S --> C (wraps around left to beginning of row)
|
|
E --> S
|
|
|
|
=========================================================================
|
|
|
|
To decrypt encrypted text, reverse the above procedure under cases #1
|
|
and #2 (move in opposite direction for substitution). Under case #3,
|
|
just take the remaining two corners of the rectangle.
|
|
|
|
|
|
Helen Fouche Gaines' classic work, ELEMENTARY CRYPTANALYSIS (1939), gives a
|
|
fairly detailed description of the Playfair Cipher and its solution methods.</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
>
|
|
</P
|
|
><P
|
|
>This script will have three main sections</P
|
|
><P
|
|
></P
|
|
><OL
|
|
TYPE="I"
|
|
><LI
|
|
><P
|
|
>Generating the <I
|
|
CLASS="FIRSTTERM"
|
|
>key square</I
|
|
>,
|
|
based on a user-input keyword.</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
>Encrypting a <I
|
|
CLASS="FIRSTTERM"
|
|
>plaintext</I
|
|
>
|
|
message.</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
>Decrypting encrypted
|
|
text.</P
|
|
></LI
|
|
></OL
|
|
><P
|
|
>The script will make extensive use of <A
|
|
HREF="arrays.html#ARRAYREF"
|
|
>arrays</A
|
|
> and <A
|
|
HREF="functions.html#FUNCTIONREF"
|
|
>functions</A
|
|
>.
|
|
You may use <A
|
|
HREF="contributed-scripts.html#GRONSFELD"
|
|
>Example A-56</A
|
|
> as an
|
|
inspiration.</P
|
|
></DD
|
|
></DL
|
|
></DIV
|
|
><P
|
|
>--</P
|
|
><P
|
|
>Please do not send the author your solutions to these
|
|
exercises. There are more appropriate ways to impress him with
|
|
your cleverness, such as submitting bugfixes and suggestions
|
|
for improving the book.</P
|
|
></DIV
|
|
><H3
|
|
CLASS="FOOTNOTES"
|
|
>Notes</H3
|
|
><TABLE
|
|
BORDER="0"
|
|
CLASS="FOOTNOTES"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
ALIGN="LEFT"
|
|
VALIGN="TOP"
|
|
WIDTH="5%"
|
|
><A
|
|
NAME="FTN.AEN25254"
|
|
HREF="writingscripts.html#AEN25254"
|
|
><SPAN
|
|
CLASS="footnote"
|
|
>[1]</SPAN
|
|
></A
|
|
></TD
|
|
><TD
|
|
ALIGN="LEFT"
|
|
VALIGN="TOP"
|
|
WIDTH="95%"
|
|
><P
|
|
>For all you clever types who failed intermediate algebra,
|
|
a <I
|
|
CLASS="FIRSTTERM"
|
|
>determinant</I
|
|
> is a numerical value
|
|
associated with a multidimensional
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>matrix</I
|
|
> (<A
|
|
HREF="arrays.html#ARRAYREF"
|
|
>array</A
|
|
> of numbers).
|
|
<TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="90%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>For the simple case of a 2 x 2 determinant:
|
|
|
|
|a b|
|
|
|b a|
|
|
|
|
The solution is a*a - b*b, where "a" and "b" represent numbers.</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
>
|
|
</P
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
><DIV
|
|
CLASS="NAVFOOTER"
|
|
><HR
|
|
ALIGN="LEFT"
|
|
WIDTH="100%"><TABLE
|
|
SUMMARY="Footer navigation table"
|
|
WIDTH="100%"
|
|
BORDER="0"
|
|
CELLPADDING="0"
|
|
CELLSPACING="0"
|
|
><TR
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="left"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="scriptanalysis.html"
|
|
ACCESSKEY="P"
|
|
>Prev</A
|
|
></TD
|
|
><TD
|
|
WIDTH="34%"
|
|
ALIGN="center"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="index.html"
|
|
ACCESSKEY="H"
|
|
>Home</A
|
|
></TD
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="right"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="revisionhistory.html"
|
|
ACCESSKEY="N"
|
|
>Next</A
|
|
></TD
|
|
></TR
|
|
><TR
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="left"
|
|
VALIGN="top"
|
|
>Analyzing Scripts</TD
|
|
><TD
|
|
WIDTH="34%"
|
|
ALIGN="center"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="exercises.html"
|
|
ACCESSKEY="U"
|
|
>Up</A
|
|
></TD
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="right"
|
|
VALIGN="top"
|
|
>Revision History</TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
></BODY
|
|
></HTML
|
|
> |