1#From: "Grigoriy Strokin" <grg@philol.msu.ru>
2#Newsgroups: comp.unix.shell
3#Subject: BASH: getopt function that parses long-named options
4#Date: Mon, 22 Dec 1997 20:35:18 +0300
5
6#Hi, I have written a BASH function named getoptex, that is like bash builtin
7#"getopts", but does parse long-named options and optional arguments. It only
8#uses builtin bash commands, so it is very fast.  In order to use it in your
9#bash scripts, include a command ". getopt.sh" (<dot> getopt.sh) to the file
10#containing your script, and that will define functions getopt, getoptex, and
11#optlistex (the file getopt.sh with its detailed description is listed
12#below).
13
14#*** file getopt.sh ***
15
16#! /bin/bash
17#
18# getopt.sh:
19# functions like getopts but do long-named options parsing
20# and support optional arguments
21#
22# Version 1.0 1997 by Grigoriy Strokin (grg@philol.msu.ru), Public Domain
23# Date created:  December 21, 1997
24# Date modified: December 21, 1997
25#
26# IMPORTANT FEATURES
27#
28# 1) Parses both short and long-named options
29# 2) Supports optional arguments
30# 3) Only uses bash builtins, thus no calls to external
31#    utilities such as expr or sed is done. Therefore,
32#    parsing speed is high enough
33#
34#
35# DESCRIPTION
36#
37# FUNCTION getopt
38# Usage: getopt OPTLIST {"$@"|ALTERNATIVE_PARAMETERS}
39#
40# like getopts, but parse options with both required and optional arguments,
41# Options with optional arguments must have "." instead of ":" after them.
42# Furthemore, a variable name to place option name cannot be specified
43# and is always placed in OPTOPT variable
44#
45# This function is provided for compatibility with getopts()
46# OPTLIST style, and it actually calls getoptex (see bellow)
47#
48# NOTE that a list of parameters is required and must be either "$@",
49# if processing command line arguments, or some alternative parameters.
50#
51# FUNCTION getoptex
52# Usage: getoptex OPTION_LIST {"$@"|ALTERNATIVE_PARAMETERS}
53#
54# like getopts, but parse long-named options.
55#
56# Both getopt and getoptex return 0 if an option has been parsed,
57# and 1 if all options are already parsed or an error occured
58#
59# Both getopt and getoptex set or test the following variables:
60#
61# OPTERR -- tested for whether error messages must be given for invalid
62options
63#
64# OPTOPT -- set to the name of an option parsed,
65#           or to "?" if no more options or error
66# OPTARG -- set to the option argument, if any;
67#           unset if ther is no argument;
68#           on error, set to the erroneous option name
69#
70# OPTIND -- Initialized to 1.
71#           Then set to the number of the next parameter to be parsed
72#           when getopt or getoptex will be called next time.
73#           When all options are parsed, contains a number of
74#           the first non-option argument.
75#
76#
77# OPTOFS -- If a parameter number $OPTIND containg an option parsed
78#           does not contain any more options, OPTOFS is unset;
79#           otherwise, OPTOFS is set to such a number of "?" signs
80#           which is equal to the number of options parsed
81#
82#           You might not set variables OPTIND and OPTOFS yourself
83#           unless you want to parse a list of parameters more than once.
84#           Otherwise, you whould unset OPTIND (or set it to 1)
85#           and unset OPTOFS each time you want to parse a new parameters
86list
87#
88# Option list format is DIFFERENT from one for getopts or getopt.
89getopts-style
90# option list can be converted to getoptex-style using a function optlistex
91# (see bellow)
92#
93# DESCRIPTION of option list used with getoptex:
94# Option names are separated by whitespace. Options consiting of
95# more than one character are treated as long-named (--option)
96#
97# Special characters can appear at the and of option names specifying
98# whether an argument is required (default is ";"):
99# ";" (default) -- no argument
100# ":" -- required argument
101# "," -- optional argument
102#
103# For example, an option list "a b c help version f: file: separator."
104# defines the following options:
105#    -a, -b, -c, --help, --version -- no argument
106#    -f, --file -- argument required
107#    --separator -- optional argument
108#
109# FUNCTION optlistex
110# Usage new_style_optlist=`optlistex OLD_STYLE_OPTLIST`
111#
112# Converts getopts-style option list in a format suitable for use with getoptex
113# Namely, it inserts spaces after each option name.
114#
115#
116# HOW TO USE
117#
118# In order o use in your bash scripts the functions described,
119# include a command ". getopt.sh" to the file containing the script,
120# which will define functions getopt, getoptex, and optlistex
121#
122# EXAMPLES
123#
124# See files 'getopt1' and 'getopt2' that contain sample scripts that use
125# getopt and getoptex functions respectively
126#
127#
128# Please send your comments to grg@philol.msu.ru
129
130function getoptex()
131{
132  let $# || return 1
133  local optlist="${1#;}"
134  let OPTIND || OPTIND=1
135  [ $OPTIND -lt $# ] || return 1
136  shift $OPTIND
137  if [ "$1" != "-" ] && [ "$1" != "${1#-}" ]
138  then OPTIND=$[OPTIND+1]; if [ "$1" != "--" ]
139  then
140    local o
141    o="-${1#-$OPTOFS}"
142    for opt in ${optlist#;}
143    do
144      OPTOPT="${opt%[;.:]}"
145      unset OPTARG
146      local opttype="${opt##*[^;:.]}"
147      [ -z "$opttype" ] && opttype=";"
148      if [ ${#OPTOPT} -gt 1 ]
149      then # long-named option
150        case $o in
151          "--$OPTOPT")
152            if [ "$opttype" != ":" ]; then return 0; fi
153            OPTARG="$2"
154            if [ -z "$OPTARG" ];
155            then # error: must have an agrument
156              let OPTERR && echo "$0: error: $OPTOPT must have an argument" >&2
157              OPTARG="$OPTOPT";
158              OPTOPT="?"
159              return 1;
160            fi
161            OPTIND=$[OPTIND+1] # skip option's argument
162            return 0
163          ;;
164          "--$OPTOPT="*)
165            if [ "$opttype" = ";" ];
166            then  # error: must not have arguments
167              let OPTERR && echo "$0: error: $OPTOPT must not have arguments" >&2
168              OPTARG="$OPTOPT"
169              OPTOPT="?"
170              return 1
171            fi
172            OPTARG=${o#"--$OPTOPT="}
173            return 0
174          ;;
175        esac
176      else # short-named option
177        case "$o" in
178          "-$OPTOPT")
179            unset OPTOFS
180            [ "$opttype" != ":" ] && return 0
181            OPTARG="$2"
182            if [ -z "$OPTARG" ]
183            then
184              echo "$0: error: -$OPTOPT must have an argument" >&2
185              OPTARG="$OPTOPT"
186              OPTOPT="?"
187              return 1
188            fi
189            OPTIND=$[OPTIND+1] # skip option's argument
190            return 0
191          ;;
192          "-$OPTOPT"*)
193            if [ $opttype = ";" ]
194            then # an option with no argument is in a chain of options
195              OPTOFS="$OPTOFS?" # move to the next option in the chain
196              OPTIND=$[OPTIND-1] # the chain still has other options
197              return 0
198            else
199              unset OPTOFS
200              OPTARG="${o#-$OPTOPT}"
201              return 0
202            fi
203          ;;
204        esac
205      fi
206    done
207    echo "$0: error: invalid option: $o"
208  fi; fi
209  OPTOPT="?"
210  unset OPTARG
211  return 1
212}
213function optlistex
214{
215  local l="$1"
216  local m # mask
217  local r # to store result
218  while [ ${#m} -lt $[${#l}-1] ]; do m="$m?"; done # create a "???..." mask
219  while [ -n "$l" ]
220  do
221    r="${r:+"$r "}${l%$m}" # append the first character of $l to $r
222    l="${l#?}" # cut the first charecter from $l
223    m="${m#?}"  # cut one "?" sign from m
224    if [ -n "${l%%[^:.;]*}" ]
225    then # a special character (";", ".", or ":") was found
226      r="$r${l%$m}" # append it to $r
227      l="${l#?}" # cut the special character from l
228      m="${m#?}"  # cut one more "?" sign
229    fi
230  done
231  echo $r
232}
233function getopt()
234{
235  local optlist=`optlistex "$1"`
236  shift
237  getoptex "$optlist" "$@"
238  return $?
239}
240
241#**************************************
242#     cut here
243#**************************************
244#*** (end of getopt.sh) ***
245
246
247#*** file getopt1 ***
248
249#! /bin/bash
250# getopt1:
251# Sample script using the function getopt
252#
253# Type something like "getopt1 -ab -d 10 -e20 text1 text2"
254# on the command line to see how it works
255#
256# See getopt.sh for more information
257#. getopt.sh
258#echo Using getopt to parse arguments:
259#while getopt "abcd:e." "$@"
260#do
261#  echo "Option <$OPTOPT> ${OPTARG:+has an arg <$OPTARG>}"
262#done
263#shift $[OPTIND-1]
264#for arg in "$@"
265#do
266#  echo "Non option argument <$arg>"
267#done
268#
269#**************************************
270#        cut here
271#**************************************
272#*** (end of getopt1) ***
273#
274#
275#*** file getopt2 ***
276#
277#! /bin/bash
278# getopt2:
279# Sample script using the function getoptex
280#
281# Type something like "getopt2 -ab -d 10 -e20 --opt1 --opt4=100 text1 text2"
282# to see how it works
283#
284# See getopt.sh for more information
285. getopt.sh
286#echo Using getoptex to parse arguments:
287#while getoptex "a; b; c; d: e. opt1 opt2 opt3 opt4: opt5." "$@"
288#do
289#  echo "Option <$OPTOPT> ${OPTARG:+has an arg <$OPTARG>}"
290#done
291#shift $[OPTIND-1]
292#for arg in "$@"
293#do
294#  echo "Non option argument <$arg>"
295#done
296#
297#**************************************
298#         cut here
299#**************************************
300#*** (end of getopt2) ***
301
302