aboutsummaryrefslogtreecommitdiffstats
path: root/examples/functions/getoptx.bash
blob: d402c7d78f04ab54f878fb288f2ef11712de08f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
#From: "Grigoriy Strokin" <grg@philol.msu.ru>
#Newsgroups: comp.unix.shell
#Subject: BASH: getopt function that parses long-named options
#Date: Mon, 22 Dec 1997 20:35:18 +0300

#Hi, I have written a BASH function named getoptex, that is like bash builtin
#"getopts", but does parse long-named options and optional arguments. It only
#uses builtin bash commands, so it is very fast.  In order to use it in your
#bash scripts, include a command ". getopt.sh" (<dot> getopt.sh) to the file
#containing your script, and that will define functions getopt, getoptex, and
#optlistex (the file getopt.sh with its detailed description is listed
#below).

#*** file getopt.sh ***

#! /bin/bash
#
# getopt.sh:
# functions like getopts but do long-named options parsing
# and support optional arguments
#
# Version 1.0 1997 by Grigoriy Strokin (grg@philol.msu.ru), Public Domain
# Date created:  December 21, 1997
# Date modified: December 21, 1997
#
# IMPORTANT FEATURES
#
# 1) Parses both short and long-named options
# 2) Supports optional arguments
# 3) Only uses bash builtins, thus no calls to external
#    utilities such as expr or sed is done. Therefore,
#    parsing speed is high enough
#
#
# DESCRIPTION
#
# FUNCTION getopt
# Usage: getopt OPTLIST {"$@"|ALTERNATIVE_PARAMETERS}
#
# like getopts, but parse options with both required and optional arguments,
# Options with optional arguments must have "." instead of ":" after them.
# Furthemore, a variable name to place option name cannot be specified
# and is always placed in OPTOPT variable
#
# This function is provided for compatibility with getopts()
# OPTLIST style, and it actually calls getoptex (see bellow)
#
# NOTE that a list of parameters is required and must be either "$@",
# if processing command line arguments, or some alternative parameters.
#
# FUNCTION getoptex
# Usage: getoptex OPTION_LIST {"$@"|ALTERNATIVE_PARAMETERS}
#
# like getopts, but parse long-named options.
#
# Both getopt and getoptex return 0 if an option has been parsed,
# and 1 if all options are already parsed or an error occured
#
# Both getopt and getoptex set or test the following variables:
#
# OPTERR -- tested for whether error messages must be given for invalid
options
#
# OPTOPT -- set to the name of an option parsed,
#           or to "?" if no more options or error
# OPTARG -- set to the option argument, if any;
#           unset if ther is no argument;
#           on error, set to the erroneous option name
#
# OPTIND -- Initialized to 1.
#           Then set to the number of the next parameter to be parsed
#           when getopt or getoptex will be called next time.
#           When all options are parsed, contains a number of
#           the first non-option argument.
#
#
# OPTOFS -- If a parameter number $OPTIND containg an option parsed
#           does not contain any more options, OPTOFS is unset;
#           otherwise, OPTOFS is set to such a number of "?" signs
#           which is equal to the number of options parsed
#
#           You might not set variables OPTIND and OPTOFS yourself
#           unless you want to parse a list of parameters more than once.
#           Otherwise, you whould unset OPTIND (or set it to 1)
#           and unset OPTOFS each time you want to parse a new parameters
list
#
# Option list format is DIFFERENT from one for getopts or getopt.
getopts-style
# option list can be converted to getoptex-style using a function optlistex
# (see bellow)
#
# DESCRIPTION of option list used with getoptex:
# Option names are separated by whitespace. Options consiting of
# more than one character are treated as long-named (--option)
#
# Special characters can appear at the and of option names specifying
# whether an argument is required (default is ";"):
# ";" (default) -- no argument
# ":" -- required argument
# "," -- optional argument
#
# For example, an option list "a b c help version f: file: separator."
# defines the following options:
#    -a, -b, -c, --help, --version -- no argument
#    -f, --file -- argument required
#    --separator -- optional argument
#
# FUNCTION optlistex
# Usage new_style_optlist=`optlistex OLD_STYLE_OPTLIST`
#
# Converts getopts-style option list in a format suitable for use with getoptex
# Namely, it inserts spaces after each option name.
#
#
# HOW TO USE
#
# In order o use in your bash scripts the functions described,
# include a command ". getopt.sh" to the file containing the script,
# which will define functions getopt, getoptex, and optlistex
#
# EXAMPLES
#
# See files 'getopt1' and 'getopt2' that contain sample scripts that use
# getopt and getoptex functions respectively
#
#
# Please send your comments to grg@philol.msu.ru

function getoptex()
{
  let $# || return 1
  local optlist="${1#;}"
  let OPTIND || OPTIND=1
  [ $OPTIND -lt $# ] || return 1
  shift $OPTIND
  if [ "$1" != "-" ] && [ "$1" != "${1#-}" ]
  then OPTIND=$[OPTIND+1]; if [ "$1" != "--" ]
  then
    local o
    o="-${1#-$OPTOFS}"
    for opt in ${optlist#;}
    do
      OPTOPT="${opt%[;.:]}"
      unset OPTARG
      local opttype="${opt##*[^;:.]}"
      [ -z "$opttype" ] && opttype=";"
      if [ ${#OPTOPT} -gt 1 ]
      then # long-named option
        case $o in
          "--$OPTOPT")
            if [ "$opttype" != ":" ]; then return 0; fi
            OPTARG="$2"
            if [ -z "$OPTARG" ];
            then # error: must have an agrument
              let OPTERR && echo "$0: error: $OPTOPT must have an argument" >&2
              OPTARG="$OPTOPT";
              OPTOPT="?"
              return 1;
            fi
            OPTIND=$[OPTIND+1] # skip option's argument
            return 0
          ;;
          "--$OPTOPT="*)
            if [ "$opttype" = ";" ];
            then  # error: must not have arguments
              let OPTERR && echo "$0: error: $OPTOPT must not have arguments" >&2
              OPTARG="$OPTOPT"
              OPTOPT="?"
              return 1
            fi
            OPTARG=${o#"--$OPTOPT="}
            return 0
          ;;
        esac
      else # short-named option
        case "$o" in
          "-$OPTOPT")
            unset OPTOFS
            [ "$opttype" != ":" ] && return 0
            OPTARG="$2"
            if [ -z "$OPTARG" ]
            then
              echo "$0: error: -$OPTOPT must have an argument" >&2
              OPTARG="$OPTOPT"
              OPTOPT="?"
              return 1
            fi
            OPTIND=$[OPTIND+1] # skip option's argument
            return 0
          ;;
          "-$OPTOPT"*)
            if [ $opttype = ";" ]
            then # an option with no argument is in a chain of options
              OPTOFS="$OPTOFS?" # move to the next option in the chain
              OPTIND=$[OPTIND-1] # the chain still has other options
              return 0
            else
              unset OPTOFS
              OPTARG="${o#-$OPTOPT}"
              return 0
            fi
          ;;
        esac
      fi
    done
    echo "$0: error: invalid option: $o"
  fi; fi
  OPTOPT="?"
  unset OPTARG
  return 1
}
function optlistex
{
  local l="$1"
  local m # mask
  local r # to store result
  while [ ${#m} -lt $[${#l}-1] ]; do m="$m?"; done # create a "???..." mask
  while [ -n "$l" ]
  do
    r="${r:+"$r "}${l%$m}" # append the first character of $l to $r
    l="${l#?}" # cut the first charecter from $l
    m="${m#?}"  # cut one "?" sign from m
    if [ -n "${l%%[^:.;]*}" ]
    then # a special character (";", ".", or ":") was found
      r="$r${l%$m}" # append it to $r
      l="${l#?}" # cut the special character from l
      m="${m#?}"  # cut one more "?" sign
    fi
  done
  echo $r
}
function getopt()
{
  local optlist=`optlistex "$1"`
  shift
  getoptex "$optlist" "$@"
  return $?
}

#**************************************
#     cut here
#**************************************
#*** (end of getopt.sh) ***


#*** file getopt1 ***

#! /bin/bash
# getopt1:
# Sample script using the function getopt
#
# Type something like "getopt1 -ab -d 10 -e20 text1 text2"
# on the command line to see how it works
#
# See getopt.sh for more information
#. getopt.sh
#echo Using getopt to parse arguments:
#while getopt "abcd:e." "$@"
#do
#  echo "Option <$OPTOPT> ${OPTARG:+has an arg <$OPTARG>}"
#done
#shift $[OPTIND-1]
#for arg in "$@"
#do
#  echo "Non option argument <$arg>"
#done
#
#**************************************
#        cut here
#**************************************
#*** (end of getopt1) ***
#
#
#*** file getopt2 ***
#
#! /bin/bash
# getopt2:
# Sample script using the function getoptex
#
# Type something like "getopt2 -ab -d 10 -e20 --opt1 --opt4=100 text1 text2"
# to see how it works
#
# See getopt.sh for more information
. getopt.sh
#echo Using getoptex to parse arguments:
#while getoptex "a; b; c; d: e. opt1 opt2 opt3 opt4: opt5." "$@"
#do
#  echo "Option <$OPTOPT> ${OPTARG:+has an arg <$OPTARG>}"
#done
#shift $[OPTIND-1]
#for arg in "$@"
#do
#  echo "Non option argument <$arg>"
#done
#
#**************************************
#         cut here
#**************************************
#*** (end of getopt2) ***