Appendix A. Contributed Scripts
These scripts, while not fitting into the text of this document, do illustrate some interesting shell programming techniques. They are useful, too. Have fun analyzing and running them.
Example A-1. manview: A script for viewing formatted man pages
1 #!/bin/bash 2 3 # Formats the source of a man page for viewing in a user directory. 4 # This is useful when writing man page source and you want to 5 # look at the intermediate results on the fly while working on it. 6 7 WRONGARGS=65 8 9 if [ -z "$1" ] 10 then 11 echo "Usage: `basename $0` [filename]" 12 exit $WRONGARGS 13 fi 14 15 groff -Tascii -man $1 | less 16 # From the man page for groff. 17 18 # If the man page includes tables and/or equations, 19 # then the above code will barf. 20 # The following line can handle such cases. 21 # 22 # gtbl < "$1" | geqn -Tlatin1 | groff -Tlatin1 -mtty-char -man 23 # 24 # Thanks, S.C. 25 26 exit 0 |
Example A-2. mailformat: Formatting an e-mail message
1 #!/bin/bash 2 3 # Format e-mail messages. 4 # Get rid of carets, tabs, also fold excessively long lines. 5 6 ARGS=1 7 E_BADARGS=65 8 NOFILE=66 9 10 if [ $# -ne $ARGS ] # Check for correct number of arguments passed to script. 11 then 12 echo "Usage: `basename $0` filename" 13 exit $E_BADARGS 14 fi 15 16 if [ -f "$1" ] # See if file exists. 17 then 18 file_name=$1 19 else 20 echo "File \"$1\" does not exist." 21 exit $NOFILE 22 fi 23 24 WIDTH=70 # Width to fold long lines to. 25 26 sed ' 27 s/^>// 28 s/^ *>// 29 s/^ *// 30 s/ *// 31 ' $1 | fold -s --width=$WIDTH 32 # -s option to fold breaks lines at spaces. 33 34 # This script was inspired by an article in a trade journal 35 # extolling a 164K Windows utility with similar functionality. 36 37 exit 0 |
Example A-3. rn: A simple-minded file rename utility
This script is a modification of Example 3-96.
1 #! /bin/bash 2 # 3 # Very simpleminded filename "rename" utility (based on "lowercase.sh"). 4 # 5 # The "ren" utility, by Vladimir Lanin (lanin@csd2.nyu.edu), 6 # does a much better job of this. 7 8 9 ARGS=2 10 E_BADARGS=65 11 ONE=1 # For getting singular/plural right (see below). 12 13 if [ $# -ne "$ARGS" ] 14 then 15 echo "Usage: `basename $0` old-pattern new-pattern" 16 # As in "rn gif jpg", which renames all gif files in working directory to jpg. 17 exit $E_BADARGS 18 fi 19 20 number=0 # Keeps track of how many files actually renamed. 21 22 23 for filename in *$1* #Traverse all matching files in directory. 24 do 25 if [ -f "$filename" ] # If finds match... 26 then 27 fname=`basename $filename` # Strip off path. 28 n=`echo $fname | sed -e "s/$1/$2/"` # Substitute new for old in filename. 29 mv $fname $n # Rename. 30 let "number += 1" 31 fi 32 done 33 34 if [ "$number" -eq "$ONE" ] # For correct grammar. 35 then 36 echo "$number file renamed." 37 else 38 echo "$number files renamed." 39 fi 40 41 exit 0 42 43 44 # Exercise for reader: 45 # What type of files will this not work on? 46 # How to fix this? |
Example A-4. encryptedpw: A script for uploading to an ftp site, using a locally encrypted password
1 #!/bin/bash 2 3 # Example 3-71 modified to use encrypted password. 4 5 if [ -z "$1" ] 6 then 7 echo "Usage: `basename $0` filename" 8 exit 65 9 fi 10 11 Username=bozo 12 # Change to suit. 13 14 Filename=`basename $1` 15 # Strips pathname out of file name 16 17 Server="XXX" 18 Directory="YYY" 19 # Change above to actual server name & directory. 20 21 22 password=`cruft <pword` 23 # "pword" is the file containing encrypted password. 24 # Uses the author's own "cruft" file encryption package, 25 # based on the classic "onetime pad" algorithm, 26 # and obtainable from: 27 # Primary-site: ftp://metalab.unc.edu /pub/Linux/utils/file 28 # cruft-0.2.tar.gz [16k] 29 30 31 ftp -n $Server <<End-Of-Session 32 # -n option disables auto-logon 33 34 user $Username $Password 35 binary 36 bell 37 # Ring 'bell' after each file transfer 38 cd $Directory 39 put $Filename 40 bye 41 End-Of-Session 42 43 exit 0 |
Example A-5. copy-cd: A script for copying a data CD
1 #!/bin/bash 2 # copy-cd.sh: copying a data CD 3 4 CDROM=/dev/cdrom # CD ROM device 5 OF=/home/bozo/projects/cdimage.iso # output file 6 # /xxxx/xxxxxxx/ Change to suit your system. 7 BLOCKSIZE=2048 8 SPEED=2 # May use higher speed if supported. 9 10 echo; echo "Insert source CD, but do *not* mount it." 11 echo "Press ENTER when ready. " 12 read ready # Wait for input, $ready not used. 13 14 echo; echo "Copying the source CD to $OF." 15 echo "This may take a while. Please be patient." 16 17 dd if=$CDROM of=$OF bs=$BLOCKSIZE # Raw device copy. 18 19 20 echo; echo "Remove data CD." 21 echo "Insert blank CDR." 22 echo "Press ENTER when ready. " 23 read ready # Wait for input, $ready not used. 24 25 echo "Copying $OF to CDR." 26 27 cdrecord -v -isosize speed=$SPEED dev=0,0 $OF 28 # Uses Joerg Schilling's "cdrecord" package (see its docs). 29 # (http://www.fokus.gmd.de/nthp/employees/schilling/cdrecord.html) 30 31 32 echo; echo "Done copying $OF to CDR on device $CDROM." 33 34 echo "Do you want to erase the image file (y/n)? " # Probably a huge file. 35 read answer 36 37 case "$answer" in 38 [yY]) rm -f $OF 39 echo "$OF erased." 40 ;; 41 *) echo "$OF not erased.";; 42 esac 43 44 echo 45 46 # Exercise for the reader: 47 # Change the above "case" statement to also accept "yes" and "Yes" as input. 48 49 exit 0 |
+
The following two scripts are by Mark Moraes of the University of Toronto. See the enclosed file "Moraes-COPYRIGHT" for permissions and restrictions.
Example A-6. behead: A script for removing mail and news message headers
1 #! /bin/sh 2 # Strips off the header from a mail/News message i.e. till the first 3 # empty line 4 # Mark Moraes, University of Toronto 5 6 # --> These comments added by author of this document. 7 8 if [ $# -eq 0 ]; then 9 # --> If no command line args present, then works on file redirected to stdin. 10 sed -e '1,/^$/d' -e '/^[ ]*$/d' 11 # --> Delete empty lines and all lines until 12 # --> first one beginning with white space. 13 else 14 # --> If command line args present, then work on files named. 15 for i do 16 sed -e '1,/^$/d' -e '/^[ ]*$/d' $i 17 # --> Ditto, as above. 18 done 19 fi 20 21 # --> Exercise for the reader: Add error checking and other options. 22 # --> 23 # --> Note that the small sed script repeats, except for the arg passed. 24 # --> Does it make sense to embed it in a function? Why or why not? |
Example A-7. ftpget: A script for downloading files via ftp
1 #! /bin/sh
2 # $Id: ftpget,v 1.2 91/05/07 21:15:43 moraes Exp $
3 # Script to perform batch anonymous ftp. Essentially converts a list of
4 # of command line arguments into input to ftp.
5 # Simple, and quick - written as a companion to ftplist
6 # -h specifies the remote host (default prep.ai.mit.edu)
7 # -d specifies the remote directory to cd to - you can provide a sequence
8 # of -d options - they will be cd'ed to in turn. If the paths are relative,
9 # make sure you get the sequence right. Be careful with relative paths -
10 # there are far too many symlinks nowadays.
11 # (default is the ftp login directory)
12 # -v turns on the verbose option of ftp, and shows all responses from the
13 # ftp server.
14 # -f remotefile[:localfile] gets the remote file into localfile
15 # -m pattern does an mget with the specified pattern. Remember to quote
16 # shell characters.
17 # -c does a local cd to the specified directory
18 # For example,
19 # ftpget -h expo.lcs.mit.edu -d contrib -f xplaces.shar:xplaces.sh \
20 # -d ../pub/R3/fixes -c ~/fixes -m 'fix*'
21 # will get xplaces.shar from ~ftp/contrib on expo.lcs.mit.edu, and put it in
22 # xplaces.sh in the current working directory, and get all fixes from
23 # ~ftp/pub/R3/fixes and put them in the ~/fixes directory.
24 # Obviously, the sequence of the options is important, since the equivalent
25 # commands are executed by ftp in corresponding order
26 #
27 # Mark Moraes (moraes@csri.toronto.edu), Feb 1, 1989
28 # --> Angle brackets changed to parens, so Docbook won't get indigestion.
29 #
30
31
32 # --> These comments added by author of this document.
33
34 # PATH=/local/bin:/usr/ucb:/usr/bin:/bin
35 # export PATH
36 # --> Above 2 lines from original script probably superfluous.
37
38 TMPFILE=/tmp/ftp.$$
39 # --> Creates temp file, using process id of script ($$)
40 # --> to construct filename.
41
42 SITE=`domainname`.toronto.edu
43 # --> 'domainname' similar to 'hostname'
44 # --> May rewrite this to parameterize this for general use.
45
46 usage="Usage: $0 [-h remotehost] [-d remotedirectory]... [-f remfile:localfile]... \
47 [-c localdirectory] [-m filepattern] [-v]"
48 ftpflags="-i -n"
49 verbflag=
50 set -f # So we can use globbing in -m
51 set x `getopt vh:d:c:m:f: $*`
52 if [ $? != 0 ]; then
53 echo $usage
54 exit 65
55 fi
56 shift
57 trap 'rm -f ${TMPFILE} ; exit' 0 1 2 3 15
58 echo "user anonymous ${USER-gnu}@${SITE} > ${TMPFILE}"
59 # --> Added quotes (recommended in complex echoes).
60 echo binary >> ${TMPFILE}
61 for i in $*
62 # --> Parse command line args.
63 do
64 case $i in
65 -v) verbflag=-v; echo hash >> ${TMPFILE}; shift;;
66 -h) remhost=$2; shift 2;;
67 -d) echo cd $2 >> ${TMPFILE};
68 if [ x${verbflag} != x ]; then
69 echo pwd >> ${TMPFILE};
70 fi;
71 shift 2;;
72 -c) echo lcd $2 >> ${TMPFILE}; shift 2;;
73 -m) echo mget "$2" >> ${TMPFILE}; shift 2;;
74 -f) f1=`expr "$2" : "\([^:]*\).*"`; f2=`expr "$2" : "[^:]*:\(.*\)"`;
75 echo get ${f1} ${f2} >> ${TMPFILE}; shift 2;;
76 --) shift; break;;
77 esac
78 done
79 if [ $# -ne 0 ]; then
80 echo $usage
81 exit 65 # --> Changed from "exit 2" to conform with standard.
82 fi
83 if [ x${verbflag} != x ]; then
84 ftpflags="${ftpflags} -v"
85 fi
86 if [ x${remhost} = x ]; then
87 remhost=prep.ai.mit.edu
88 # --> Rewrite to match your favorite ftp site.
89 fi
90 echo quit >> ${TMPFILE}
91 # --> All commands saved in tempfile.
92
93 ftp ${ftpflags} ${remhost} < ${TMPFILE}
94 # --> Now, tempfile batch processed by ftp.
95
96 rm -f ${TMPFILE}
97 # --> Finally, tempfile deleted (you may wish to copy it to a logfile).
98
99
100 # --> Exercises for reader:
101 # --> 1) Add error checking.
102 # --> 2) Add bells & whistles. |
+
Antek Sawicki contributed the following script, which makes very clever use of the parameter substitution operators discussed in Section 3.3.1.
Example A-8. password: A script for generating random 8-character passwords
1 #!/bin/bash
2 # May need to be invoked with #!/bin/bash2 on older machines.
3 #
4 # Random password generator for bash 2.x by Antek Sawicki <tenox@tenox.tc>,
5 # who generously gave permission to the document author to use it here.
6 #
7 # ==> Comments added by document author ==>
8
9
10 MATRIX="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
11 LENGTH="8"
12 # ==> May change 'LENGTH' for longer password, of course.
13
14
15 while [ "${n:=1}" -le "$LENGTH" ]
16 # ==> Recall that := is "default substitution" operator.
17 # ==> So, if 'n' has not been initialized, set it to 1.
18 do
19 PASS="$PASS${MATRIX:$(($RANDOM%${#MATRIX})):1}"
20 # ==> Very clever, but tricky.
21
22 # ==> Starting from the innermost nesting...
23 # ==> ${#MATRIX} returns length of array MATRIX.
24 # ==> $RANDOM%${#MATRIX} returns random number between 1 and length of MATRIX - 1.
25
26 # ==> ${MATRIX:$(($RANDOM%${#MATRIX})):1}
27 # ==> returns expansion of MATRIX at random position, by length 1.
28 # ==> See {var:pos:len} parameter substitution in Section 3.3.1 and following examples.
29
30 # ==> PASS=... simply pastes this result onto previous PASS (concatenation).
31
32 # ==> To visualize this more clearly, uncomment the following line
33 # ==> echo "$PASS"
34 # ==> to see PASS being built up, one character at a time, each iteration of the loop.
35
36 let n+=1
37 # ==> Increment 'n' for next pass.
38 done
39
40 echo "$PASS"
41 #== Or, redirect to file, as desired. |
+
James R. Van Zandt contributed this script, which uses named pipes and, in his words, "really exercises quoting and escaping".
Example A-9. fifo: A script for making daily backups, using named pipes
1 #!/bin/bash
2 # ==> Script by James R. Van Zandt, and used here with his permission.
3
4 # ==> Comments added by author of this document.
5
6
7 HERE=`uname -n`
8 # ==> hostname
9 THERE=bilbo
10 echo "starting remote backup to $THERE at `date +%r`"
11 # ==> `date +%r` returns time in 12-hour format, i.e. "08:08:34 PM".
12
13 # make sure /pipe really is a pipe and not a plain file
14 rm -rf /pipe
15 mkfifo /pipe
16 # ==> Create a "named pipe", named "/pipe".
17
18 # ==> 'su xyz' runs commands as user "xyz".
19 # ==> 'ssh' invokes secure shell (remote login client).
20 su xyz -c "ssh $THERE \"cat >/home/xyz/backup/${HERE}-daily.tar.gz\" < /pipe"&
21 cd /
22 tar -czf - bin boot dev etc home info lib man root sbin share usr var >/pipe
23 # ==> Uses named pipe, /pipe, to communicate between processes:
24 # ==> 'tar/gzip' writes to /pipe and 'ssh' reads from /pipe.
25
26 # ==> The end result is this backs up the main directories, from / on down.
27
28 # ==> What are the advantages of a "named pipe" in this situation,
29 # ==> as opposed to an "anonymous pipe", with |?
30 # ==> Will an anonymous pipe even work here?
31
32
33 exit 0 |
+
Stephane Chazelas contributed the following script to demonstrate that generating prime numbers does not require arrays.
Example A-10. Generating prime numbers using the modulo operator
1 #!/bin/bash
2 # primes.sh: generate prime numbers, without using arrays.
3
4 # This does *not* use the classic "Sieve of Erastosthenes" algorithm,
5 # but instead uses the more intuitive method of testing each candidate number
6 # for factors (divisors) up to half its value, using the "%" modulo operator.
7 #
8 # Script contributed by Stephane Chazelas,
9
10
11 LIMIT=1000 # Primes 2 - 1000
12
13 Primes()
14 {
15 (( n = $1 + 1 )) # Bump to next integer.
16 shift
17
18 if (( n == LIMIT ))
19 then echo $*
20 return
21 fi
22
23 for i; do
24 (( i * i > n )) && break # Need check divisors only halfway to top.
25 (( n % i )) && continue # Sift out non-primes using modulo operator.
26 Primes $n $@ # Recursion.
27 return
28 done
29
30 Primes $n $@ $n # Recursion.
31 }
32
33 Primes 1
34
35 # This may also be rewritten without recursion for faster execution.
36
37 exit 0
38
39 # Compare the speed of this algorithm for generating primes
40 # with the Sieve of Erastosthenes (ex68.sh). |
+
Jordi Sanfeliu gave permission to use his elegant tree script.
Example A-11. tree: A script for displaying a directory tree
1 #!/bin/sh
2 # @(#) tree 1.1 30/11/95 by Jordi Sanfeliu
3 # email: mikaku@arrakis.es
4 #
5 # Initial version: 1.0 30/11/95
6 # Next version : 1.1 24/02/97 Now, with symbolic links
7 # Patch by : Ian Kjos, to support unsearchable dirs
8 # email: beth13@mail.utexas.edu
9 #
10 # Tree is a tool for view the directory tree (obvious :-) )
11 #
12
13 # ==> 'Tree' script used here with the permission of its author, Jordi Sanfeliu.
14 # ==> Comments added by the author of this document.
15 # ==> Argument quoting added.
16
17
18 search () {
19 for dir in `echo *`
20 # ==> `echo *` lists all the files in current working directory, without line breaks.
21 # ==> Similar effect to for dir in *
22 # ==> but "dir in `echo *`" will not handle filenames with blanks.
23 do
24 if [ -d "$dir" ] ; then # ==> If it is a directory (-d)...
25 zz=0 # ==> Temp variable, keeping track of directory level.
26 while [ $zz != $deep ] # Keep track of inner nested loop.
27 do
28 echo -n "| " # ==> Display vertical connector symbol,
29 # ==> with 2 spaces & no line feed in order to indent.
30 zz=`expr $zz + 1` # ==> Increment zz.
31 done
32 if [ -L "$dir" ] ; then # ==> If directory is a symbolic link...
33 echo "+---$dir" `ls -l $dir | sed 's/^.*'$dir' //'`
34 # ==> Display horiz. connector and list directory name, but...
35 # ==> delete date/time part of long listing.
36 else
37 echo "+---$dir" # ==> Display horizontal connector symbol...
38 # ==> and print directory name.
39 if cd "$dir" ; then # ==> If can move to subdirectory...
40 deep=`expr $deep + 1` # ==> Increment depth.
41 search # with recursivity ;-)
42 # ==> Function calls itself.
43 numdirs=`expr $numdirs + 1` # ==> Increment directory count.
44 fi
45 fi
46 fi
47 done
48 cd .. # ==> Up one directory level.
49 if [ "$deep" ] ; then # ==> If depth = 0 (returns TRUE)...
50 swfi=1 # ==> set flag showing that search is done.
51 fi
52 deep=`expr $deep - 1` # ==> Decrement depth.
53 }
54
55 # - Main -
56 if [ $# = 0 ] ; then
57 cd `pwd` # ==> No args to script, then use current working directory.
58 else
59 cd $1 # ==> Otherwise, move to indicated directory.
60 fi
61 echo "Initial directory = `pwd`"
62 swfi=0 # ==> Search finished flag.
63 deep=0 # ==> Depth of listing.
64 numdirs=0
65 zz=0
66
67 while [ "$swfi" != 1 ] # While flag not set...
68 do
69 search # ==> Call function after initializing variables.
70 done
71 echo "Total directories = $numdirs"
72
73
74
75 # ==> Challenge to reader: try to figure out exactly how this script works. |
Noah Friedman gave permission to use his string function script, which essentially reproduces some of the C-library string manipulation functions.
Example A-12. string functions: C-like string functions
1 #!/bin/bash
2
3 # string.bash --- bash emulation of string(3) library routines
4 # Author: Noah Friedman <friedman@prep.ai.mit.edu>
5 # ==> Used with his kind permission in this document.
6 # Created: 1992-07-01
7 # Last modified: 1993-09-29
8 # Public domain
9
10 # Conversion to bash v2 syntax done by Chet Ramey
11
12 # Commentary:
13 # Code:
14
15 #:docstring strcat:
16 # Usage: strcat s1 s2
17 #
18 # Strcat appends the value of variable s2 to variable s1.
19 #
20 # Example:
21 # a="foo"
22 # b="bar"
23 # strcat a b
24 # echo $a
25 # => foobar
26 #
27 #:end docstring:
28
29 ###;;;autoload
30 function strcat ()
31 {
32 local s1_val s2_val
33
34 s1_val=${!1} # indirect variable expansion
35 s2_val=${!2}
36 eval "$1"=\'"${s1_val}${s2_val}"\'
37 # ==> eval $1='${s1_val}${s2_val}' avoids problems,
38 # ==> if one of the variables contains a single quote.
39 }
40
41 #:docstring strncat:
42 # Usage: strncat s1 s2 $n
43 #
44 # Line strcat, but strncat appends a maximum of n characters from the value
45 # of variable s2. It copies fewer if the value of variabl s2 is shorter
46 # than n characters. Echoes result on stdout.
47 #
48 # Example:
49 # a=foo
50 # b=barbaz
51 # strncat a b 3
52 # echo $a
53 # => foobar
54 #
55 #:end docstring:
56
57 ###;;;autoload
58 function strncat ()
59 {
60 local s1="$1"
61 local s2="$2"
62 local -i n="$3"
63 local s1_val s2_val
64
65 s1_val=${!s1} # ==> indirect variable expansion
66 s2_val=${!s2}
67
68 if [ ${#s2_val} -gt ${n} ]; then
69 s2_val=${s2_val:0:$n} # ==> substring extraction
70 fi
71
72 eval "$s1"=\'"${s1_val}${s2_val}"\'
73 # ==> eval $1='${s1_val}${s2_val}' avoids problems,
74 # ==> if one of the variables contains a single quote.
75 }
76
77 #:docstring strcmp:
78 # Usage: strcmp $s1 $s2
79 #
80 # Strcmp compares its arguments and returns an integer less than, equal to,
81 # or greater than zero, depending on whether string s1 is lexicographically
82 # less than, equal to, or greater than string s2.
83 #:end docstring:
84
85 ###;;;autoload
86 function strcmp ()
87 {
88 [ "$1" = "$2" ] && return 0
89
90 [ "${1}" '<' "${2}" ] > /dev/null && return -1
91
92 return 1
93 }
94
95 #:docstring strncmp:
96 # Usage: strncmp $s1 $s2 $n
97 #
98 # Like strcmp, but makes the comparison by examining a maximum of n
99 # characters (n less than or equal to zero yields equality).
100 #:end docstring:
101
102 ###;;;autoload
103 function strncmp ()
104 {
105 if [ -z "${3}" -o "${3}" -le "0" ]; then
106 return 0
107 fi
108
109 if [ ${3} -ge ${#1} -a ${3} -ge ${#2} ]; then
110 strcmp "$1" "$2"
111 return $?
112 else
113 s1=${1:0:$3}
114 s2=${2:0:$3}
115 strcmp $s1 $s2
116 return $?
117 fi
118 }
119
120 #:docstring strlen:
121 # Usage: strlen s
122 #
123 # Strlen returns the number of characters in string literal s.
124 #:end docstring:
125
126 ###;;;autoload
127 function strlen ()
128 {
129 eval echo "\${#${1}}"
130 # ==> Returns the length of the value of the variable
131 # ==> whose name is passed as an argument.
132 }
133
134 #:docstring strspn:
135 # Usage: strspn $s1 $s2
136 #
137 # Strspn returns the length of the maximum initial segment of string s1,
138 # which consists entirely of characters from string s2.
139 #:end docstring:
140
141 ###;;;autoload
142 function strspn ()
143 {
144 # Unsetting IFS allows whitespace to be handled as normal chars.
145 local IFS=
146 local result="${1%%[!${2}]*}"
147
148 echo ${#result}
149 }
150
151 #:docstring strcspn:
152 # Usage: strcspn $s1 $s2
153 #
154 # Strcspn returns the length of the maximum initial segment of string s1,
155 # which consists entirely of characters not from string s2.
156 #:end docstring:
157
158 ###;;;autoload
159 function strcspn ()
160 {
161 # Unsetting IFS allows whitspace to be handled as normal chars.
162 local IFS=
163 local result="${1%%[${2}]*}"
164
165 echo ${#result}
166 }
167
168 #:docstring strstr:
169 # Usage: strstr s1 s2
170 #
171 # Strstr echoes a substring starting at the first occurrence of string s2 in
172 # string s1, or nothing if s2 does not occur in the string. If s2 points to
173 # a string of zero length, strstr echoes s1.
174 #:end docstring:
175
176 ###;;;autoload
177 function strstr ()
178 {
179 # if s2 points to a string of zero length, strstr echoes s1
180 [ ${#2} -eq 0 ] && { echo "$1" ; return 0; }
181
182 # strstr echoes nothing if s2 does not occur in s1
183 case "$1" in
184 *$2*) ;;
185 *) return 1;;
186 esac
187
188 # use the pattern matching code to strip off the match and everything
189 # following it
190 first=${1/$2*/}
191
192 # then strip off the first unmatched portion of the string
193 echo "${1##$first}"
194 }
195
196 #:docstring strtok:
197 # Usage: strtok s1 s2
198 #
199 # Strtok considers the string s1 to consist of a sequence of zero or more
200 # text tokens separated by spans of one or more characters from the
201 # separator string s2. The first call (with a non-empty string s1
202 # specified) echoes a string consisting of the first token on stdout. The
203 # function keeps track of its position in the string s1 between separate
204 # calls, so that subsequent calls made with the first argument an empty
205 # string will work through the string immediately following that token. In
206 # this way subsequent calls will work through the string s1 until no tokens
207 # remain. The separator string s2 may be different from call to call.
208 # When no token remains in s1, an empty value is echoed on stdout.
209 #:end docstring:
210
211 ###;;;autoload
212 function strtok ()
213 {
214 :
215 }
216
217 #:docstring strtrunc:
218 # Usage: strtrunc $n $s1 {$s2} {$...}
219 #
220 # Used by many functions like strncmp to truncate arguments for comparison.
221 # Echoes the first n characters of each string s1 s2 ... on stdout.
222 #:end docstring:
223
224 ###;;;autoload
225 function strtrunc ()
226 {
227 n=$1 ; shift
228 for z; do
229 echo "${z:0:$n}"
230 done
231 }
232
233 # provide string
234
235 # string.bash ends here
236
237
238 # ========================================================================== #
239 # ==> Everything below here added by the document author.
240
241 # ==> Suggested use of this script is to delete everything below here,
242 # ==> and "source" this file into your own scripts.
243
244 # strcat
245 string0=one
246 string1=two
247 echo
248 echo "Testing \"strcat\" function:"
249 echo "Original \"string0\" = $string0"
250 echo "\"string1\" = $string1"
251 strcat string0 string1
252 echo "New \"string0\" = $string0"
253 echo
254
255 # strlen
256 echo
257 echo "Testing \"strlen\" function:"
258 str=123456789
259 echo "\"str\" = $str"
260 echo -n "Length of \"str\" = "
261 strlen str
262 echo
263
264
265
266 # Exercise for reader:
267 # Add code to test all the other string functions above.
268
269
270 exit 0 |
Stephane Chazelas demonstrates object-oriented programming a Bash script.
Example A-13. Object-oriented database
1 #!/bin/bash
2 # Object-oriented programming in a shell script.
3
4 # Script by Stephane Chazelas.
5
6
7 person.new() # Looks almost like a class declaration in C++.
8 {
9 local obj_name=$1 name=$2 firstname=$3 birthdate=$4
10
11 eval "$obj_name.set_name() {
12 eval \"$obj_name.get_name() {
13 echo \$1
14 }\"
15 }"
16
17 eval "$obj_name.set_firstname() {
18 eval \"$obj_name.get_firstname() {
19 echo \$1
20 }\"
21 }"
22
23 eval "$obj_name.set_birthdate() {
24 eval \"$obj_name.get_birthdate() {
25 echo \$1
26 }\"
27 eval \"$obj_name.show_birthdate() {
28 echo \$(date -d \"1/1/1970 0:0:\$1 GMT\")
29 }\"
30 eval \"$obj_name.get_age() {
31 echo \$(( (\$(date +%s) - \$1) / 3600 / 24 / 365 ))
32 }\"
33 }"
34
35 $obj_name.set_name $name
36 $obj_name.set_firstname $firstname
37 $obj_name.set_birthdate $birthdate
38 }
39
40 echo
41
42 person.new self Bozeman Bozo 101272413
43 # Create an instance of "person.new" (actually passing args to the function).
44
45 self.get_firstname # Bozo
46 self.get_name # Bozeman
47 self.get_age # 28
48 self.get_birthdate # 101272413
49 self.show_birthdate # Sat Mar 17 20:13:33 MST 1973
50
51 echo
52
53 # typeset -f
54 # to see the created functions (careful, it scrolls off the page).
55
56 exit 0 |
