- •Table of Contents
- •Chapter 1. Why Shell Programming?
- •2.1. Invoking the script
- •2.2. Preliminary Exercises
- •Part 2. Basics
- •Chapter 3. Exit and Exit Status
- •Chapter 4. Special Characters
- •Chapter 5. Introduction to Variables and Parameters
- •5.1. Variable Substitution
- •5.2. Variable Assignment
- •5.3. Bash Variables Are Untyped
- •5.4. Special Variable Types
- •Chapter 6. Quoting
- •Chapter 7. Tests
- •7.1. Test Constructs
- •7.2. File test operators
- •7.3. Comparison operators (binary)
- •7.4. Nested if/then Condition Tests
- •7.5. Testing Your Knowledge of Tests
- •Chapter 8. Operations and Related Topics
- •8.1. Operators
- •8.2. Numerical Constants
- •Part 3. Beyond the Basics
- •Chapter 9. Variables Revisited
- •9.1. Internal Variables
- •9.2. Manipulating Strings
- •9.2.1. Manipulating strings using awk
- •9.2.2. Further Discussion
- •9.3. Parameter Substitution
- •9.4. Typing variables: declare or typeset
- •9.5. Indirect References to Variables
- •9.6. $RANDOM: generate random integer
- •9.7. The Double Parentheses Construct
- •Chapter 10. Loops and Branches
- •10.1. Loops
- •10.2. Nested Loops
- •10.3. Loop Control
- •10.4. Testing and Branching
- •Chapter 11. Internal Commands and Builtins
- •11.1. Job Control Commands
- •Chapter 12. External Filters, Programs and Commands
- •12.1. Basic Commands
- •12.2. Complex Commands
- •12.3. Time / Date Commands
- •12.4. Text Processing Commands
- •12.5. File and Archiving Commands
- •12.6. Communications Commands
- •12.7. Terminal Control Commands
- •12.8. Math Commands
- •12.9. Miscellaneous Commands
- •Chapter 13. System and Administrative Commands
- •Chapter 14. Command Substitution
- •Chapter 15. Arithmetic Expansion
- •Chapter 16. I/O Redirection
- •16.1. Using exec
- •16.2. Redirecting Code Blocks
- •16.3. Applications
- •Chapter 17. Here Documents
- •Chapter 18. Recess Time
- •Part 4. Advanced Topics
- •Chapter 19. Regular Expressions
- •19.1. A Brief Introduction to Regular Expressions
- •19.2. Globbing
- •Chapter 20. Subshells
- •Chapter 21. Restricted Shells
- •Chapter 22. Process Substitution
- •Chapter 23. Functions
- •23.1. Complex Functions and Function Complexities
- •23.2. Local Variables
- •23.2.1. Local variables make recursion possible.
- •Chapter 24. Aliases
- •Chapter 25. List Constructs
- •Chapter 26. Arrays
- •Chapter 27. Files
- •Chapter 28. /dev and /proc
- •28.2. /proc
- •Chapter 29. Of Zeros and Nulls
- •Chapter 30. Debugging
- •Chapter 31. Options
- •Chapter 32. Gotchas
- •Chapter 33. Scripting With Style
- •33.1. Unofficial Shell Scripting Stylesheet
- •Chapter 34. Miscellany
- •34.2. Shell Wrappers
- •34.3. Tests and Comparisons: Alternatives
- •34.4. Optimizations
- •34.5. Assorted Tips
- •34.6. Oddities
- •34.7. Portability Issues
- •34.8. Shell Scripting Under Windows
- •Chapter 35. Bash, version 2
- •Chapter 36. Endnotes
- •36.1. Author's Note
- •36.2. About the Author
- •36.3. Tools Used to Produce This Book
- •36.3.1. Hardware
- •36.3.2. Software and Printware
- •36.4. Credits
- •Bibliography
- •Appendix A. Contributed Scripts
- •Appendix C. Exit Codes With Special Meanings
- •Appendix D. A Detailed Introduction to I/O and I/O Redirection
- •Appendix E. Localization
- •Appendix F. History Commands
- •Appendix G. A Sample .bashrc File
- •Appendix H. Converting DOS Batch Files to Shell Scripts
- •Appendix I. Exercises
- •Appendix J. Copyright
Advanced Bash−Scripting Guide
This utility records (saves to a file) all the user keystrokes at the command line in a console or an xterm window. This, in effect, create a record of a session.
12.8. Math Commands
Command Listing
factor
Decompose an integer into prime factors.
bash$ factor 27417
27417: 3 13 19 37
bc, dc
These are flexible, arbitrary precision calculation utilities.
bc has a syntax vaguely resembling C.
dc uses RPN ("Reverse Polish Notation").
Of the two, bc seems more useful in scripting. It is a fairly well−behaved UNIX utility, and may therefore be used in a pipe.
Bash can't handle floating point calculations, and it lacks operators for certain important mathematical functions. Fortunately, bc comes to the rescue.
Here is a simple template for using bc to calculate a script variable. This uses command substitution.
variable=$(echo "OPTIONS; OPERATIONS" | bc)
Example 12−28. Monthly Payment on a Mortgage
#!/bin/bash
#monthlypmt.sh: Calculates monthly payment on a mortgage.
#This is a modification of code in the "mcalc" (mortgage calculator) package,
#by Jeff Schmidt and Mendel Cooper (yours truly, the author of this document).
#http://www.ibiblio.org/pub/Linux/apps/financial/mcalc−1.6.tar.gz [15k]
echo
echo "Given the principal, interest rate, and term of a mortgage," echo "calculate the monthly payment."
bottom=1.0
echo
echo −n "Enter principal (no commas) " read principal
12.8. Math Commands |
178 |
Advanced Bash−Scripting Guide
echo −n "Enter interest rate (percent) " # If 12%, enter "12", not ".12". read interest_r
echo −n "Enter term (months) " read term
interest_r=$(echo "scale=9; $interest_r/100.0" | bc) # Convert to decimal.
# "scale" determines how many decimal places.
interest_rate=$(echo "scale=9; $interest_r/12 + 1.0" | bc)
top=$(echo "scale=9; $principal*$interest_rate^$term" | bc)
echo; echo "Please be patient. This may take a while."
let "months = $term − 1"
for ((x=$months; x > 0; x−−)) do
bot=$(echo "scale=9; $interest_rate^$x" | bc) bottom=$(echo "scale=9; $bottom+$bot" | bc)
# bottom = $(($bottom + $bot")) done
# let "payment = $top/$bottom" payment=$(echo "scale=2; $top/$bottom" | bc)
# Use two decimal places for dollars and cents.
echo
echo "monthly payment = \$$payment" # Echo a dollar sign in front of amount. echo
exit 0
#Exercises:
#1) Filter input to permit commas in principal amount.
#2) Filter input to permit interest to be entered as percent or decimal.
#3) If you are really ambitious,
#expand this script to print complete amortization tables.
Example 12−29. Base Conversion
:
##########################################################################
# Shellscript: |
base.sh − print number to different bases (Bourne Shell) |
||
# Author |
: |
Heiner Steven (heiner.steven@odn.de) |
|
# |
Date |
: |
07−03−95 |
# |
Category |
: |
Desktop |
#$Id: base.sh,v 1.2 2000/02/06 19:55:35 heiner Exp $
##########################################################################
#Description
#
#Changes
#21−03−95 stv fixed error occuring with 0xb as input (0.2)
##########################################################################
#==> Used in this document with the script author's permission.
#==> Comments added by document author.
12.8. Math Commands |
179 |
Advanced Bash−Scripting Guide
NOARGS=65 |
|
|
PN=`basename "$0"` |
|
# Program name |
VER=`echo '$Revision: 1.2 |
$' | cut −d' ' −f2` |
# ==> VER=1.2 |
Usage () {
echo "$PN − print number to different bases, $VER (stv '95) usage: $PN [number ...]
If no number is given, the numbers are read from standard input.
A number may be |
|
|
binary (base 2) |
starting with 0b |
(i.e. 0b1100) |
octal (base 8) |
starting with 0 |
(i.e. 014) |
hexadecimal (base 16) |
starting with 0x |
(i.e. 0xc) |
decimal |
otherwise (i.e. 12)" >&2 |
|
exit $NOARGS |
|
|
}# ==> Function to print usage message.
Msg () { |
|
|
|
for i |
# ==> in [list] missing. |
|
|
do echo |
"$PN: $i" >&2 |
|
|
done |
|
|
|
} |
|
|
|
Fatal () { Msg "$@"; exit 66; } |
|
|
|
PrintBases () { |
|
|
|
# Determine base of the number |
|
||
for i |
# ==> in [list] missing... |
|
|
do |
# ==> so operates on command line arg(s). |
||
case "$i" in |
|
|
|
|
0b*) |
ibase=2;; |
# binary |
|
0x*|[a−f]*|[A−F]*) |
ibase=16;; |
# hexadecimal |
|
0*) |
ibase=8;; |
# octal |
|
[1−9]*) |
ibase=10;; |
# decimal |
|
*) |
|
|
Msg "illegal number $i − ignored" continue;;
esac
#Remove prefix, convert hex digits to uppercase (bc needs this) number=`echo "$i" | sed −e 's:^0[bBxX]::' | tr '[a−f]' '[A−F]'`
#==> Uses ":" as sed separator, rather than "/".
#Convert number to decimal
dec=`echo "ibase=$ibase; $number" | bc` |
# ==> 'bc' is calculator utility. |
|
case "$dec" in |
|
|
[0−9]*) |
;; |
# number ok |
*) |
continue;; |
# error: ignore |
esac |
|
|
#Print all conversions in one line.
#==> 'here document' feeds command list to 'bc'. echo `bc <<!
obase=16; "hex="; $dec obase=10; "dec="; $dec obase=8; "oct="; $dec obase=2; "bin="; $dec
!
` | sed −e 's: : :g'
done
}
12.8. Math Commands |
180 |
Advanced Bash−Scripting Guide
while [ $# −gt 0 ] |
|
|
do |
|
|
case "$1" in |
|
|
−−) |
shift; break;; |
|
−h) |
Usage;; |
# ==> Help message. |
−*) |
Usage;; |
|
*) |
break;; |
# first number |
esac |
# ==> More error checking for illegal input would be useful. |
|
shift |
|
|
done |
|
|
if [ $# −gt 0 ] |
|
|
then |
|
|
PrintBases "$@" |
|
|
else |
|
# read from stdin |
while read line |
|
|
do |
|
|
PrintBases $line
done
fi
An alternate method of invoking bc involves using a here document embedded within a command substitution block. This is especially appropriate when a script needs to pass a list of options and commands to bc.
variable=`bc << LIMIT_STRING options
statements operations LIMIT_STRING
`
...or...
variable=$(bc << LIMIT_STRING options
statements operations LIMIT_STRING
)
Example 12−30. Another way to invoke bc
#!/bin/bash
#Invoking 'bc' using command substitution
#in combination with a 'here document'.
var1=`bc << EOF |
|
18.33 * 19.78 |
|
EOF |
|
` |
|
echo $var1 |
# 362.56 |
# $( ... ) notation also works. v1=23.53
v2=17.881
12.8. Math Commands |
181 |
Advanced Bash−Scripting Guide
v3=83.501
v4=171.63
var2=$(bc << EOF |
|
scale = 4 |
|
a = ( $v1 + $v2 ) |
|
b = ( $v3 * $v4 ) |
|
a * b + 15.35 |
|
EOF |
|
) |
|
echo $var2 |
# 593487.8452 |
var3=$(bc −l << EOF scale = 9
s ( 1.7 ) EOF
)
#Returns the sine of 1.7 radians.
#The "−l" option calls the 'bc' math library.
echo $var3 |
# |
.991664810 |
|
# Now, try it in |
a |
function... |
|
hyp= |
# |
Declare global variable. |
|
hypotenuse () |
# |
Calculate hypotenuse of a right triangle. |
|
{ |
|
|
|
hyp=$(bc −l << EOF |
|
|
|
scale = 9 |
|
|
|
sqrt ( $1 * $1 + |
$2 * $2 ) |
|
|
EOF |
|
|
|
) |
|
|
|
# Unfortunately, |
can't return floating point values from a Bash function. |
||
} |
|
|
|
hypotenuse 3.68 7.31 |
|
||
echo "hypotenuse |
= |
$hyp" |
# 8.184039344 |
exit 0
awk
Yet another way of doing floating point math in a script is using awk's built−in math functions in a shell wrapper.
Example 12−31. Calculating the hypotenuse of a triangle
#!/bin/bash
# hypotenuse.sh: Returns the "hypotenuse" of a right triangle.
# |
( square root of sum |
of squares of the "legs") |
|
ARGS=2 |
# |
Script needs |
sides of triangle passed. |
E_BADARGS=65 |
# |
Wrong number |
of arguments. |
if [ $# −ne "$ARGS" ] # Test number of arguments to script. then
echo "Usage: `basename $0` side_1 side_2" exit $E_BADARGS
fi
12.8. Math Commands |
182 |