diff --git a/LDP/guide/docbook/abs-guide/agram.sh b/LDP/guide/docbook/abs-guide/agram.sh new file mode 100644 index 00000000..82ee3f96 --- /dev/null +++ b/LDP/guide/docbook/abs-guide/agram.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# agram.sh: Playing games with anagrams. + +# Find anagrams of... +LETTERSET=etaoinshrdlu + +anagram "$LETTERSET" | # Find all anagrams of the letterset... +grep '.......' | # With at least 7 letters, +grep '^is' | # starting with 'is' +grep -v 's$' | # no plurals +grep -v 'ed$' # no past tense verbs + +# Uses "anagram" utility +#+ that is part of the author's "yawl" word list package. +# http://ibiblio.org/pub/Linux/libs/yawl-0.2.tar.gz + +exit 0 # End of code. + +bash$ sh agram.sh +islander +isolate +isolead +isotheral diff --git a/LDP/guide/docbook/abs-guide/cannon.sh b/LDP/guide/docbook/abs-guide/cannon.sh new file mode 100644 index 00000000..40c06f0d --- /dev/null +++ b/LDP/guide/docbook/abs-guide/cannon.sh @@ -0,0 +1,119 @@ +#!/bin/bash +# cannon.sh: Approximating PI by firing cannonballs. + +# This is a very simple instance of a "Monte Carlo" simulation, +#+ a mathematical model of a real-life event, +#+ using pseudorandom numbers to emulate random chance. + +# Consider a perfectly square plot of land, 10000 units on a side. +# This land has a perfectly circular lake in its center, +#+ with a diameter of 10000 units. +# The plot is actually all water, except for the four corners. +# (Think of it as a square with an inscribed circle.) +# +# Let us fire solid iron cannonballs from an old-style cannon +#+ at the square of land. +# All the shots impact somewhere on the plot of land, +#+ either in the lake or on the dry corners. +# Since the lake takes up most of the land area, +#+ most of the shots will SPLASH! into the water. +# Just a few shots will THUD! into solid ground +#+ in the far corners of the land. +# +# If we take enough random, unaimed shots at the plot of land, +#+ Then the ratio of SPLASHES to total shots will approximate +#+ the value of PI/4 +# Exercise: Explain why. +# +# Theoretically, the more shots taken, the better the fit. +# However, a shell script, as opposed to a compiled language +#+ with floating-point math built in, requires a few compromises. +# This tends to make the simulation less accurate, unfortunately. + + +DIMENSION=10000 # Length of each side of the plot of land. + # Also sets ceiling for random integers generated. + +MAXSHOTS=1000 # Fire this many shots. + # 10000 or more would be better, but would take too long. +PMULTIPLIER=4.0 # Scaling factor to approximate PI. + +get_random () +{ +SEED=$(head -1 /dev/urandom | od -N 1 | awk '{ print $2 }') +RANDOM=$SEED # From "seeding-random.sh" + #+ example script. +let "rnum = $RANDOM % $DIMENSION" # Range less than 10000. +echo $rnum +} + +distance= # Declare global variable. +hypotenuse () # Calculate hypotenuse of a right triangle. +{ # From "alt-bc.sh" example. +distance=$(bc -l << EOF +scale = 0 +sqrt ( $1 * $1 + $2 * $2 ) +EOF +) +# Setting "scale" to zero rounds down result to integer value, +#+ a necessary compromise in this script. +# This diminshes the accuracy of the simulation, unfortunately. +} + + +# main() { + +# Initialize variables. +shots=0 +splashes=0 +thuds=0 +Pi=0 + +while [ "$shots" -lt "$MAXSHOTS" ] # Main loop. +do + + xCoord=$(get_random) # Get random X and Y coords. + yCoord=$(get_random) + hypotenuse $xCoord $yCoord # Hypotenuse of right-triangle = + #+ distance. + ((shots++)) + + printf "#%4d " $shots + printf "Xc = %4d " $xCoord + printf "Yc = %4d " $yCoord + printf "Distance = %5d " $distance # Distance from center + #+ of lake. + + if [ "$distance" -le "$DIMENSION" ] + then + echo -n "SPLASH! " + ((splashes++)) + else + echo -n "THUD! " + ((thuds++)) + fi + + Pi=$(echo "scale=9; $PMULTIPLIER*$splashes/$shots" | bc) + # Multiply ratio by 4.0. + echo -n "PI ~ $Pi" + echo + +done + +echo +echo "After $shots shots, PI looks like approximately $Pi." +# Tends to run a bit high... +# Probably due to round-off error and imperfect randomness of $RANDOM. +echo + +# } + +exit 0 + +# One might well wonder whether a shell script is appropriate for +#+ an application as complex and computation-intensive as a simulation. +# +# There are at least two justifications. +# 1) As a proof of concept: to show it can be done. +# 2) To prototype and test the algorithms before rewriting +#+ it in a compiled high-level language. diff --git a/LDP/guide/docbook/abs-guide/color-echo.sh b/LDP/guide/docbook/abs-guide/color-echo.sh new file mode 100644 index 00000000..c7a3da4e --- /dev/null +++ b/LDP/guide/docbook/abs-guide/color-echo.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# color-echo.sh: Echoing text messages in color. + +# Modify this script for your own purposes. +# It's easier than hand-coding color. + +black='\E[30;47m' +red='\E[31;47m' +green='\E[32;47m' +yellow='\E[33;47m' +blue='\E[34;47m' +magenta='\E[35;47m' +cyan='\E[36;47m' +white='\E[37;47m' + + +alias Reset="tput sgr0" # Reset text attributes to normal + #+ without clearing screen. + + +cecho () # Color-echo. + # Argument $1 = message + # Argument $1 = color +{ +local default_msg="No message passed." + # Doesn't really need to be a local variable. + +message=${1:-$default_msg} # Defaults to default message. +color=${2:-$black} # Defaults to black, if not specified. + + echo -e "$color" + echo "$message" + Reset # Reset to normal. + + return +} + + +# Now, let's try it out. +# ---------------------------------------------------- +cecho "Feeling blue..." $blue +cecho "Magenta looks more like purple." $magenta +cecho "Green with envy." $green +cecho "Seeing red?" $red +cecho "Cyan, more familiarly known as aqua." $cyan +cecho "No color passed (defaults to black)." + # Missing $color argument. +cecho "\"Empty\" color passed (defaults to black)." "" + # Empty $color argument. +cecho + # Missing $message and $color arguments. +cecho "" "" + # Empty $message and $color arguments. +# ---------------------------------------------------- + +echo + +exit 0 + +# Exercises: +# --------- +# 1) Add the "bold" attribute to the 'cecho ()' function. +# 2) Add options for colored backgrounds. diff --git a/LDP/guide/docbook/abs-guide/ex30a.sh b/LDP/guide/docbook/abs-guide/ex30a.sh new file mode 100644 index 00000000..655e0327 --- /dev/null +++ b/LDP/guide/docbook/abs-guide/ex30a.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# ex30a.sh: "Colorized" version of ex30.sh. +# Crude address database + + +clear # Clear the screen. + +echo -n " " +echo -e '\E[37;44m'"\033[1mContact List\033[0m" + # White on blue background +echo; echo +echo -e "\033[1mChoose one of the following persons:\033[0m" + # Bold +tput sgr0 +echo "(Enter only the first letter of name.)" +echo +echo -en '\E[47;34m'"\033[1mE\033[0m" # Blue +tput sgr0 # Reset colors to "normal." +echo "vans, Roland" # "[E]vans, Roland" +echo -en '\E[47;35m'"\033[1mJ\033[0m" # Magenta +tput sgr0 +echo "ones, Mildred" +echo -en '\E[47;32m'"\033[1mS\033[0m" # Green +tput sgr0 +echo "mith, Julie" +echo -en '\E[47;31m'"\033[1mZ\033[0m" # Red +tput sgr0 +echo "ane, Morris" +echo + +read person + +case "$person" in +# Note variable is quoted. + + "E" | "e" ) + # Accept upper or lowercase input. + echo + echo "Roland Evans" + echo "4321 Floppy Dr." + echo "Hardscrabble, CO 80753" + echo "(303) 734-9874" + echo "(303) 734-9892 fax" + echo "revans@zzy.net" + echo "Business partner & old friend" + ;; + + "J" | "j" ) + echo + echo "Mildred Jones" + echo "249 E. 7th St., Apt. 19" + echo "New York, NY 10009" + echo "(212) 533-2814" + echo "(212) 533-9972 fax" + echo "milliej@loisaida.com" + echo "Girlfriend" + echo "Birthday: Feb. 11" + ;; + +# Add info for Smith & Zane later. + + * ) + # Default option. + # Empty input (hitting RETURN) fits here, too. + echo + echo "Not yet in database." + ;; + +esac + +tput sgr0 # Reset colors to "normal." + +echo + +exit 0 diff --git a/LDP/guide/docbook/abs-guide/poem.sh b/LDP/guide/docbook/abs-guide/poem.sh new file mode 100644 index 00000000..b2fdfd32 --- /dev/null +++ b/LDP/guide/docbook/abs-guide/poem.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# poem.sh + +# Lines of the poem (single stanza). +Line[1]="I do not know which to prefer," +Line[2]="The beauty of inflections" +Line[3]="Or the beauty of innuendoes," +Line[4]="The blackbird whistling" +Line[5]="Or just after." + +# Attribution. +Attrib[1]=" Wallace Stevens" +Attrib[2]="\"Thirteen Ways of Looking at a Blackbird\"" + +for index in 1 2 3 4 5 # Five lines. +do + printf " %s\n" "${Line[index]}" +done + +for index in 1 2 # Two attribution lines. +do + printf " %s\n" "${Attrib[index]}" +done + +exit 0 diff --git a/LDP/guide/docbook/abs-guide/self-source.sh b/LDP/guide/docbook/abs-guide/self-source.sh new file mode 100644 index 00000000..105dda1f --- /dev/null +++ b/LDP/guide/docbook/abs-guide/self-source.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# self-source.sh: a script sourcing itself "recursively." +# From "Stupid Script Tricks," Volume II. + +MAXPASSCNT=100 # Maximum number of execution passes. + +echo -n "$pass_count " +# At first execution pass, this just echoes two blank spaces, +#+ since $pass_count still uninitialized. + +let "pass_count += 1" +# Assumes the uninitialized variable $pass_count +#+ can be incremented the first time around. +# This works with Bash and pdksh, but +#+ it relies on non-portable (and possibly dangerous) behavior. +# Better would be to set $pass_count to 0 if non-initialized. + +while [ "$pass_count" -le $MAXPASSCNT ] +do + . $0 # Script "sources" itself, rather than calling itself. + # ./$0 (which would be true recursion) doesn't work here. +done + +# What occurs here is not actually recursion, +#+ since the script effectively "expands" itself +#+ (generates a new section of code) +#+ with each pass throught the 'while' loop', +# with each 'source' in line 20. +# +# Of course, the script interprets each newly 'sourced' "#!" line +#+ as a comment, and not as the start of a new script. + +echo + +exit 0 # The net effect is counting from 1 to 100. + # Very impressive. + +# Exercise: +# -------- +# Write a script that uses this trick to do something useful. diff --git a/LDP/guide/docbook/abs-guide/soundex.sh b/LDP/guide/docbook/abs-guide/soundex.sh new file mode 100644 index 00000000..43cdb138 --- /dev/null +++ b/LDP/guide/docbook/abs-guide/soundex.sh @@ -0,0 +1,157 @@ +#!/bin/bash +# soundex.sh: Calculate "soundex" code for names + +# ======================================================= +# Soundex script +# by +# Mendel Cooper +# thegrendel@theriver.com +# 23 January, 2002 +# +# Placed in the Public Domain. +# +# A slightly different version of this script appeared in +#+ Ed Schaefer's July, 2002 "Shell Corner" column +#+ in "Unix Review" on-line, +#+ http://www.unixreview.com/documents/uni1026336632258/ +# ======================================================= + + +ARGCOUNT=1 # Need name as argument. +E_WRONGARGS=70 + +if [ $# -ne "$ARGCOUNT" ] +then + echo "Usage: `basename $0` name" + exit $E_WRONGARGS +fi + + +assign_value () # Assigns numerical value +{ #+ to letters of name. + + val1=bfpv # 'b,f,p,v' = 1 + val2=cgjkqsxz # 'c,g,j,k,q,s,x,z' = 2 + val3=dt # etc. + val4=l + val5=mn + val6=r + +# Exceptionally clever use of 'tr' follows. +# Try to figure out what is going on here. + +value=$( echo "$1" \ +| tr -d wh \ +| tr $val1 1 | tr $val2 2 | tr $val3 3 \ +| tr $val4 4 | tr $val5 5 | tr $val6 6 \ +| tr -s 123456 \ +| tr -d aeiouy ) + +# Assign letter values. +# Remove duplicate numbers, except when separated by vowels. +# Ignore vowels, except as separators, so delete them last. +# Ignore 'w' and 'h', even as separators, so delete them first. +# +# The above command substitution lays more pipe than a plumber <g>. + +} + + +input_name="$1" +echo +echo "Name = $input_name" + + +# Change all characters of name input to lowercase. +# ------------------------------------------------ +name=$( echo $input_name | tr A-Z a-z ) +# ------------------------------------------------ +# Just in case argument to script is mixed case. + + +# Prefix of soundex code: first letter of name. +# -------------------------------------------- + + +char_pos=0 # Initialize character position. +prefix0=${name:$char_pos:1} +prefix=`echo $prefix0 | tr a-z A-Z` + # Uppercase 1st letter of soundex. + +let "char_pos += 1" # Bump character position to 2nd letter of name. +name1=${name:$char_pos} + + +# ++++++++++++++++++++++++++ Exception Patch +++++++++++++++++++++++++++++++++ +# Now, we run both the input name and the name shifted one char to the right +#+ through the value-assigning function. +# If we get the same value out, that means that the first two characters +#+ of the name have the same value assigned, and that one should cancel. +# However, we also need to test whether the first letter of the name +#+ is a vowel or 'w' or 'h', because otherwise this would bollix things up. + +char1=`echo $prefix | tr A-Z a-z` # First letter of name, lowercased. + +assign_value $name +s1=$value +assign_value $name1 +s2=$value +assign_value $char1 +s3=$value +s3=9$s3 # If first letter of name is a vowel + #+ or 'w' or 'h', + #+ then its "value" will be null (unset). + #+ Therefore, set it to 9, an otherwise + #+ unused value, which can be tested for. + + +if [[ "$s1" -ne "$s2" || "$s3" -eq 9 ]] +then + suffix=$s2 +else + suffix=${s2:$char_pos} +fi +# ++++++++++++++++++++++ end Exception Patch +++++++++++++++++++++++++++++++++ + + +padding=000 # Use at most 3 zeroes to pad. + + +soun=$prefix$suffix$padding # Pad with zeroes. + +MAXLEN=4 # Truncate to maximum of 4 chars. +soundex=${soun:0:$MAXLEN} + +echo "Soundex = $soundex" + +echo + +# The soundex code is a method of indexing and classifying names +#+ by grouping together the ones that sound alike. +# The soundex code for a given name is the first letter of the name, +#+ followed by a calculated three-number code. +# Similar sounding names should have almost the same soundex codes. + +# Examples: +# Smith and Smythe both have a "S-530" soundex. +# Harrison = H-625 +# Hargison = H-622 +# Harriman = H-655 + +# This works out fairly well in practice, but there are numerous anomalies. +# +# +# The U.S. Census and certain other governmental agencies use soundex, +# as do genealogical researchers. +# +# For more information, +#+ see the "National Archives and Records Administration home page", +#+ http://www.nara.gov/genealogy/soundex/soundex.html + + + +# Exercise: +# -------- +# Simplify the "Exception Patch" section of this script. + +exit 0 diff --git a/LDP/guide/docbook/abs-guide/tempfile-name.sh b/LDP/guide/docbook/abs-guide/tempfile-name.sh new file mode 100644 index 00000000..6f898d34 --- /dev/null +++ b/LDP/guide/docbook/abs-guide/tempfile-name.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# tempfile-name.sh: temp filename generator + +BASE_STR=`mcookie` # 32-character magic cookie. +POS=11 # Arbitrary position in magic cookie string. +LEN=5 # Get $LEN consecutive characters. + +prefix=temp # This is, after all, a "temp" file. + # For more "uniqueness," generate the filename prefix + #+ using the same method as the suffix, below. + +suffix=${BASE_STR:POS:LEN} + # Extract a 5-character string, starting at position 11. + +temp_filename=$prefix.$suffix + # Construct the filename. + +echo "Temp filename = "$temp_filename"" + +# sh tempfile-name.sh +# Temp filename = temp.e19ea + +exit 0 diff --git a/LDP/guide/docbook/abs-guide/unit-conversion.sh b/LDP/guide/docbook/abs-guide/unit-conversion.sh new file mode 100644 index 00000000..f519353f --- /dev/null +++ b/LDP/guide/docbook/abs-guide/unit-conversion.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# unit-conversion.sh + + +convert_units () # Takes as arguments the units to convert. +{ + cf=$(units "$1" "$2" | sed --silent -e '1p' | awk '{print $2}') + # Strip off everything except the actual conversion factor. + echo "$cf" +} + +Unit1=miles +Unit2=meters +cfactor=`convert_units $Unit1 $Unit2` +quantity=3.73 + +result=$(echo $quantity*$cfactor | bc) + +echo "There are $result $Unit2 in $quantity $Unit1." + +# What happens if you pass incompatible units, +#+ such as "acres" and "miles" to the function? + +exit 0 diff --git a/LDP/guide/docbook/abs-guide/usage-message.sh b/LDP/guide/docbook/abs-guide/usage-message.sh new file mode 100644 index 00000000..43280084 --- /dev/null +++ b/LDP/guide/docbook/abs-guide/usage-message.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# usage-message.sh + +: ${1?"Usage: $0 ARGUMENT"} +# Script exits here if command-line parameter absent, +#+ with following error message. +# usage-message.sh: 1: Usage: usage-message.sh ARGUMENT + +echo "These two lines echo only if command-line parameter given." +echo "command line parameter = \"$1\"" + +exit 0 # Will exit here only if command-line parameter present. + +# Check the exit status, both with and without command-line parameter. +# If command-line parameter present, then "$?" is 0. +# If not, then "$?" is 1.