- •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
It is even possible to combine a Bash script and Perl script within the same file. Depending on how the script is invoked, either the Bash part or the Perl part will execute.
Example 34−5. Bash and Perl scripts combined
#!/bin/bash
# bashandperl.sh
echo "Greetings from the Bash part of the script."
# More Bash commands may follow here.
exit 0
#End of Bash part of the script.
#=======================================================
#!/usr/bin/perl
# This part of the script must be invoked with −x option.
print "Greetings from the Perl part of the script.\n";
#More Perl commands may follow here.
#End of Perl part of the script.
bash$ bash bashandperl.sh
Greetings from the Bash part of the script.
bash$ perl −x bashandperl.sh
Greetings from the Perl part of the script.
34.3. Tests and Comparisons: Alternatives
For tests, the [[ ]] construct may be more appropriate than [ ]. Likewise, arithmetic comparisons might benefit from the (( )) construct.
a=8 |
|
|
# All of the comparisons below are equivalent. |
|
|
test "$a" −lt 16 && echo "yes, $a < 16" |
# "and list" |
|
/bin/test "$a" −lt |
16 && echo "yes, $a < 16" |
|
[ "$a" −lt 16 ] && |
echo "yes, $a < 16" |
|
[[ $a −lt 16 ]] && |
echo "yes, $a < 16" |
# Quoting variables within |
(( a < 16 )) && echo "yes, $a < 16" |
# [[ ]] and (( )) not necessary. |
city="New York"
# Again, all of the comparisons below are equivalent.
test "$city" \< Paris && echo "Yes, Paris is greater than $city" # Greater ASCII order. /bin/test "$city" \< Paris && echo "Yes, Paris is greater than $city"
[ |
"$city" \< Paris ] && echo "Yes, Paris is greater than $city" |
|
[[ $city < Paris ]] && echo "Yes, Paris is greater than $city" |
# Need not quote $city. |
|
# |
Thank you, S.C. |
|
|
|
|
34.3. Tests and Comparisons: Alternatives |
303 |
Advanced Bash−Scripting Guide
34.4. Optimizations
Most shell scripts are quick 'n dirty solutions to non−complex problems. As such, optimizing them for speed is not much of an issue. Consider the case, though, where a script carries out an important task, does it well, but runs too slowly. Rewriting it in a compiled language may not be a palatable option. The simplest fix would be to rewrite the parts of the script that slow it down. Is it possible to apply principles of code optimization even to a lowly shell script?
Check the loops in the script. Time consumed by repetitive operations adds up quickly. Use the time and times tools to profile computation−intensive commands. Consider rewriting time−critical code sections in C, or even in assembler.
Try to minimize file i/o. Bash is not particularly efficient at handling files, so consider using more appropriate tools for this within the script, such as awk or Perl.
Try to write your scripts in a structured, coherent form, so they can be reorganized and tightened up as necessary. Some of the optimization techniques applicable to high−level languages may work for scripts, but others, such as loop unrolling, are mostly irrelevant. Above all, use common sense.
34.5. Assorted Tips
∙To keep a record of which user scripts have run during a particular sesssion or over a number of sessions, add the following lines to each script you want to keep track of. This will keep a continuing file record of the script names and invocation times.
# Append (>>) following to end of each script tracked.
date>> |
$SAVE_FILE |
#Date and time. |
echo $0>> $SAVE_FILE |
#Script name. |
|
echo>> |
$SAVE_FILE |
#Blank line as separator. |
#Of course, SAVE_FILE defined and exported as environmental variable in ~/.bashrc
#(something like ~/.scripts−run)
∙
The >> operator appends lines to a file. What if you wish to prepend a line to an existing file, that is, to paste it in at the beginning?
file=data.txt
title="***This is the title line of data text file***"
echo $title | cat − $file >$file.new
#"cat −" concatenates stdout to $file.
#End result is
#+ to write a new file with $title appended at *beginning*.
Of course, sed can also do this.
∙A shell script may act as an embedded command inside another shell script, a Tcl or wish script, or even a Makefile. It can be invoked as as an external shell command in a C program using the system() call, i.e., system("script_name");.
34.4. Optimizations |
304 |
Advanced Bash−Scripting Guide
∙ Put together files containing your favorite and most useful definitions and functions. As necessary, "include" one or more of these "library files" in scripts with either the dot (.) or source command.
#SCRIPT LIBRARY
#−−−−−− −−−−−−−
#Note:
#No "#!" here.
#No "live code" either.
#Useful variable definitions
ROOT_UID=0 |
# |
Root has $UID 0. |
E_NOTROOT=101 |
# |
Not root user error. |
MAXRETVAL=256 |
# Maximum (positive) return value of a function. |
|
SUCCESS=0 |
|
|
FAILURE=−1 |
|
|
# Functions |
|
|
Usage () |
# "Usage:" message. |
|
{ |
|
|
if [ −z "$1" ] |
# No arg passed. |
|
then |
|
|
msg=filename |
|
|
else |
|
|
msg=$@ |
|
|
fi |
|
|
echo "Usage: `basename $0` "$msg"" |
||
} |
|
|
Check_if_root () |
# Check if root running script. |
|
{ |
# From "ex39.sh" example. |
|
if [ "$UID" −ne "$ROOT_UID" ] |
||
then |
|
|
echo "Must be root to run this script." |
||
exit $E_NOTROOT |
|
|
fi |
|
|
} |
|
|
CreateTempfileName () |
# Creates a "unique" temp filename. |
|
{ |
# From "ex51.sh" example. |
|
prefix=temp |
|
|
suffix=`eval date +%s` |
|
|
Tempfilename=$prefix.$suffix |
||
} |
|
|
isalpha2 () |
# Tests whether *entire string* is alphabetic. |
|
{ |
# From "isalpha.sh" example. |
[ $# −eq 1 ] || return $FAILURE
case $1 in
*[!a−zA−Z]*|"") return $FAILURE;; *) return $SUCCESS;;
34.4. Optimizations |
305 |
Advanced Bash−Scripting Guide
esac |
# Thanks, |
S.C. |
} |
|
|
abs () |
|
# Absolute value. |
{ |
|
# Caution: Max return value = 256. |
E_ARGERR=−999999 |
|
|
if [ |
−z "$1" ] |
# Need arg passed. |
then |
|
|
return $E_ARGERR |
# Obvious error value returned. |
|
fi |
|
|
if [ |
"$1" −ge 0 ] |
# If non−negative, |
then |
|
# |
absval=$1 |
# stays as−is. |
|
else |
|
# Otherwise, |
let "absval = (( 0 − $1 ))" |
# change sign. |
|
fi |
|
|
return $absval
}
∙ Use special−purpose comment headers to increase clarity and legibility in scripts.
## |
Caution. |
|
|
rm −rf *.zzy ## |
The "−rf" options to "rm" are very dangerous, |
||
|
##+ |
especially with wildcards. |
|
#+ Line continuation. |
|
||
# |
This is line 1 |
|
|
#+ |
of a multi−line |
comment, |
|
#+ |
and this is the |
final line. |
|
#* |
Note. |
|
|
#o |
List item. |
|
|
#> Another point of view. |
|
||
while [ "$var1" != |
"end" ] |
#> while test "$var1" != "end" |
∙Using the $? exit status variable, a script may test if a parameter contains only digits, so it can be treated as an integer.
#!/bin/bash
SUCCESS=0
E_BADINPUT=65
test "$1" −ne 0 −o "$1" −eq 0 2>/dev/null
#An integer is either equal to 0 or not equal to 0.
#2>/dev/null suppresses error message.
if [ $? −ne "$SUCCESS" ] then
echo "Usage: `basename $0` integer−input" exit $E_BADINPUT
fi
let "sum = $1 + 25" echo "Sum = $sum"
# Would give error if $1 not integer.
# Any variable, not just a command line parameter, can be tested this way.
34.4. Optimizations |
306 |