function my_getopts { version="3.141592, 2002-02-02, Michael Wang ." typeset PATH= SEP= i typeset -u _I for i; do _I=$i eval ${_I%%?(:)=*}= unset ${_I%%?(:)=*} case $_I in *[!:]=*) eval $(IFS=$SEP; print ${_I%%=*}=\"${_I#*=}\") ;; *:=*) eval $(IFS=$SEP; print ${_I%%:=*}=\"${i#*:=}\") ;; esac done } ## =head1 NAME ## ## my_getopts - parse command line options ## ## =head1 SYNOPSIS ## ## Write my_prog as: ## ## function my_getopts { ...; } ## my_getopts B ... "$@" ## ... ## ## or ## ## FPATH=/some/where/my_getopts/resizes ## my_getopts B ... "$@" ## ... ## ## Run my_prog as: ## ## my_prog B ... ## ## =head1 DESCRIPTION ## ## B is a function which is used inside a program ## (my_prog in SYNOPSIS) to process the program's command line ## options. It can be either referenced by FPATH and loaded in ## from filesystem, or statically included in the program. ## ## The B can be in any of the following forms: ## ## key=val KEY=VAL # no perserve case ## key:=VaL KEY=VaL # do perserve case ## key[:]="val1 val2 ..." ## sep= key=val1val2val3... ## sep= key:=val1val2val3... ## ## The latter option overwrite the previous one, this feature can be used ## to initialize or specify the default values for certain keys. The last ## sep= is in effect for later options. The sep="" is default. ## ## The following are my_getopts design points: ## ## - Unlike the classical "loop and shift" method, my_getopts handles ## variables generically; that is, variable names do not need to be ## known before the my_getopts function call. my_getopts handles: ## ## VARIABLE=VALUE ## ## without ever mentioning VARIABLE. ## ## - my_getopts upshifts variable names, and upshifts values using "=". ## Using "=:" preserves the variable case. ## ## - As a design issue, I always use uppercase variable names because I don't ## want to remember whether to use: ## ## my_getopts _show=Y ## ## or ## ## my_getopts _SHOW=Y ## ## - my_getopts uses a seperator, SEP, to set a variable equal to multiple ## values. ## ## Assume the test script my_prog.ss has access to the my_getopts function: ## ## # my_prog.ss ## # insert the my_getopts function or set FPATH to find the function. ## my_getopts "$@" # process the command line options. ## ## print "SID variable: $SID" ## print "OWNER variable: $OWNER" ## print "EMAIL variable: $EMAIL" ## print "SQL variable: $SQL" ## print "TABLE variable: $TABLE" ## # end my_prog.ss ## ## Executing my_prog.ss: ## ## my_prog.ss sid:=MySID owner=MySelf email:="a B" sep=: \ ## Sql:=MySQL:arg1:arg2 sep=, table=tab1:p1,tab2:p2 ## ## yields: ## ## SID variable: MySID ## OWNER variable: MYSELF ## EMAIL variable: a B ## SQL variable: MySQL arg1 arg2 ## TABLE variable: TAB1:P1 TAB2:P2 ## ## Although a casual user of my_getopts doesn't need to understand ## how it works, I'll describe in detail how my_getopts functions: ## ## Inside the my_getopts function, I define variable _I as uppercase: ## ## typeset -u _I ## ## which upshifts each command-line option; For example: ## ## _I=_ShoW=y ## print $_I ## _SHOW=Y ## ## Shell pattern ${_I%%?(:)=*} cuts from the rear of the argument greedily ## (meaning the longest match) 0 or 1 ":" followed by "=" followed by ## anything. Again, using the _ShoW=y example: ## ## $ print ${_I%%?(:)=*} ## _SHOW ## ## So ${_I%%?(:)=*}= unset ${_I%%?(:)=*} evalutes to _SHOW= unset _SHOW. ## ## Why unset _SHOW? This is defensive programing. _SHOW may have been ## exported by the parent program with some other lowercase value. By ## resetting the variable, a script is not affected by any unexpected ## environment setting. ## ## I use eval to construct and execute commands in the current shell. ## Instead of using Ksh patterns, I could manipulate variable ${_I} by ## "tr", "sed", or another external program. I cite the shell programming ## principle of doing things within the shell, which is not only more ## efficient, but more portable. Note that I set PATH to null inside the ## function, so my_getopts does not depend on any external Unix utility or ## program. ## ## The case statement tests whether _I is the form of "key=VaL" or ## "key:=VaL". If it is key=val, I convert it to KEY=VAL and do not ## preserve the case. If it is key:=val, I convert it to KEY=VaL ## preserving the case of the value. ## ## ${_I%%:=*} and ${_I%%=*} was explained above; it gets the "key" ## part. ${_I#*=} and ${i#*:=} get the "val" part with the former ## converting the "val" to uppercase, and the latter preserving the case. ## ## Use double quotes in \"${_I#*=}\" and =\"${i#*:=}\" because the ## "val" part may not be a simple value. It may be multiple values ## seperated by specified seperators. For example: ## ## my_prog sep=: Sql:=MySQL:arg1:arg2 ## ## is rewritten to SQL="MySQL arg1 arg2". ## ## Again, you can use an external program to split the "var" part ## (e.g., MySQL:arg1:arg2), but since I do as much as possible inside the ## shell, I split the fields with field-separator IFS: ## ## a=MySQL:arg1:arg2 ## print $a ## MySQL:arg1:arg2 ## IFS=: ## print $a ## MySQL arg1 arg2 ## ## In normal shell programming, IFS isn't permanently changed, but ## saved, used, and later, restored: ## ## OIFS=$IFS ## IFS=: ## ... ## IFS=$OIFS ## ## But not in the my_getopts function; here, IFS is set and used in a subshell: ## ## ... eval $(IFS=$SEP; print ....) ## ## Any variable change in a subshell doesn't affect the parent. ## ## Unfortunately, my_getopts doesn't port to the Bash shell without ## major changes. ## ## Bash Shell my_getopts ## ## The following function is the Bash version of my_getopts: ## ## function my_getopts_bash { ## #- version 3.141592, 2002-02-02, Michael Wang . ## typeset PATH=$(PATH=/bin:/usr/bin getconf PATH) SEP= i ## for i; do ## _I=$(echo $i | tr "[:lower:]" "[:upper:]") ## _J=$(echo $_I | sed "s/:\{0,1\}=.*//") ## eval ${_J}= unset ${_J} ## case $_I in ## *[!:]=*) eval $(IFS=$SEP; echo ${_I%%=*}=\"${_I#*=}\") ;; ## *:=*) eval $(IFS=$SEP; echo ${_I%%:=*}=\"${i#*:=}\") ;; ## esac ## done ## } ## ## While the patterns in the case statement do not change between ## shells, the upshifting and the unsetting must change: ## ## The Bash shell doesn't support "typset -u", so I elect to use: ## ## tr "[:lower:]" "[:upper:]" ## ## Since the Bash shell has no print command, I replace print with echo. ## ## The unset part uses ksh pattern, ?(:)=*, which matches ":=anything" ## and "=anything", does not work under bash. I elected to replace the ksh ## construct with: ## ## sed "s/:\{0,1\}=.*//" ## ## Unfortunately, the function now uses external commands, so PATH can ## no longer be empty. I typeset a localized PATH definition inside the ## function so the function finds the POSIX-compliant tr and sed commands. ## Using POSIX-compliant external commands ensures the my_getopts_bash ## function works under a variety of Unix platforms that support POSIX ## standard. ## ## The getconf (get configuration values) command is a method to set ## PATH to POSIX-compliant directories. For example, on Solaris: ## ## getconf PATH ## ## yields POSIX directories: ## ## /usr/xpg4/bin:/usr/ccs/bin:/usr/bin:/opt/SUNWspro/bin ## ## so the sed and tr commands located in /usr/xpg4/bin execute instead ## of those in /usr/bin. ## ## PATH=/bin:/usr/bin getconf PATH ## ## is a way to bootstrap PATH. Before PATH is set, the getconf command ## must be located. If getconf isn't a built-in command, the function ## locates it in /bin or /usr/bin. Generally, getconf is an external ## command under the ksh88 shell, but is built-in in the newer ksh93. ## ## In conclusion, portability comes with a price namely, the ## performance and dependency associated with the use of the external tr ## and sed commands. With freely available ksh, in both source and binary ## forms, on nearly every Unix system, you must decide how you want to ## define portability. ## ## =head1 SEE ALSO ## ## getopt(1), getopts(1). ## ## http://www.unixreview.com/documents/s=7781/uni1042138723500/ ## ## =head1 AUTHORS ## ## Michael Wang >. ## ## This is free software. You may copy or redistribute it under the same ## terms as Perl itself. ## ## If you modify it, you are required to send a copy of the modification to ## the author via email. ## ## =head1 VERSION HISTORY ## ## version 3.141592, 2002-02-02, Michael Wang . ## * use #- to indicate the current version in _show=y. ## * use SEP= (instead of SEP=:) as the default. ## * fixed problem that _help=y produces extra lines in perldoc. ## * tested /usr/perl5/bin/perldoc. ## * Enhanced the man page. ## ## version 3.14159, 2001-12-02, Michael Wang . ## * changed I to _I to be able to process I=. ## * changed MAN=Y to _HELP=Y and added _SHOW=Y. ## * made _HELP=Y and _SHOW=Y work with ksh88 and ksh93. ## ## version 3.1415, 04/17/2001, Michael Wang . ## * added online man page. ## ## version 3.141, 03/21/2001, Michael Wang . ## * added sep= option, demonstrated in example. ## ## version 3.14, 03/14/2001, Michael Wang . ## * my_getopts: my command options parser. ## * key=val => KEY=VAL no perserve case. ## * key:=VaL => KEY=VaL do perserve case.