-
Notifications
You must be signed in to change notification settings - Fork 0
/
parsOpts.sh
256 lines (249 loc) · 12 KB
/
parsOpts.sh
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
# Title: parsOpts
# Description: parsOpts is a bash function that can be used to parse the positional parameters of a shell script
# into options and their corresponding arguments
# Author: Andrew T. Withers
# Set OPTIND global variable to zero
OPTIND=0
parsOpts()
{
# Unset OPT and OPTARG global variables
unset OPT
unset OPTARG
# Add all arguments (shell arguments + function argument) to the $args array
local args=("$@")
# Set optstring (Possible options and arguments) to the last element in $args
local optstring="${args[(($#-1))]}"
# Remove the last element in args (optstring)
unset "args[${#args[@]}-1]"
## Parse optstring
# Send the options and their argument declarations to an array
local optstringarr=($(echo $optstring | sed -e 's/,/ /g'))
# Create an associative array in which the option is the key and the number of arguments for that option is the value. '-1' represents unknown number of options
declare -A opt_args
for i in "${optstringarr[@]}"; do
if [[ $(printf "%s" "$i" | grep -o ':' | grep -c ':') -eq 2 ]]; then # Number of args is unknown (double ':')
opt_args[$(printf "%s" "$i" | cut -d':' -f1)]=-1
elif [[ $(printf "%s" "$i" | grep -c ':') -eq 0 ]]; then # Number of args is 0 (no ':')
opt_args["$i"]=0
elif [[ $(printf "%s" "$i" | grep -o ':' | grep -c ':') -eq 1 ]] && [[ $(printf "%s" "$i" | cut -d':' -f2) =~ ^[0-9]+$ ]]; then
opt_args[$(printf "%s" "$i" | cut -d':' -f1)]=$(printf "%s" "$i" | cut -d':' -f2) # Number of args is equal to the number following ':'
elif [[ $(printf "%s" "$i" | grep -o ':' | grep -c ':') -eq 1 ]] && [[ $(printf "%s" "$i" | cut -c 3) == '' ]]; then
opt_args[$(printf "%s" "$i" | cut -d':' -f1)]=1 # Number of args is 1 (single ':')
else
OPTARG="$i"
OPT='!'
printf "parsOpts: Error while parsing OPTSTRING. Undefined number of args for option: %s." "$OPTARG"
printf "Ensure that the OPTSTRING is properly formatted."
break 2
fi
done
# Validate the integrity of the optstring. Ensure that each key has an integer value
for i in "${!opt_args[@]}"; do
if ! [[ ${opt_args["$i"]} =~ ^[+-]?[0-9]+$ ]] || [[ ${opt_args["$i"]} -lt -1 ]]; then
OPTARG="$i"
OPT='!'
printf "parsOpts: Error while parsing OPTSTRING. Undefined number of args for option: %s." "$OPTARG"
printf "Ensure that the OPTSTRING is properly formatted."
break 2
fi
done
## Parse option
if [[ $(printf "%s" "${args[$OPTIND]}" | head -c1) != '-' ]]; then
break
else
# Check if more stringed short options require processing. Note: ____Stringed_Short_Index accounts for the '-' prefix in the stringed option (args[$OPTIND])
if [[ -n $____Stringed_Short_Index ]] && [[ $____Stringed_Short_Index -le $(printf "%s" "${args[$OPTIND]}" | wc -c) ]]; then
# Get the next opt from the string and increment the stringed short option index
OPT=$(printf "%s" "${args[$OPTIND]}" | cut -c "$____Stringed_Short_Index")
((____Stringed_Short_Index+=1))
# Check that the OPT is a key in opt_args
if echo "${!opt_args[@]}" | grep -qw "$OPT"; then
# Find the number of arguments that should be assosciated with this option
local numargs="${opt_args[$OPT]}"
if [[ $numargs =~ ^[1-9]+$ ]]; then # numargs is a positive integer. The option requires a specific number of args
# Add the numargs number of arguments to the OPTARG string. If too few arguments exist, return OPT as OPTARG, OPT as ':', and NUMARGS
local arg_count=0
for (( i=$((OPTIND+1)); i<=$((OPTIND+numargs)); i++ )); do
if [[ $(printf "%s" "${args["$i"]}" | head -c1) != '-' ]] && [[ -n ${args["$i"]} ]]; then
OPTARG+="${args[$i]} "
((arg_count++))
else
OPTARG="$OPT"
OPT=':'
NUMARGS="$numargs"
# Change numargs to the actual number of arguments that were detected
numargs=$arg_count
break
fi
done
if [[ $OPT != ':' ]] && [[ $(printf "%s" "$OPTARG" | rev | head -c1) == ' ' ]]; then # The appropriate number of arguments were present
OPTARG=$(printf "%s" "$OPTARG" | rev | cut -c 2- | rev) # Remove the trailing space at the end of the OPTARG string
fi
elif [[ $numargs -eq -1 ]]; then # An unknown number of arguments are attached to this option
numargs=0
i=$((OPTIND+1))
while [[ $(printf "%s" "${args["$i"]}" | head -c1) != '-' ]] && [[ -n ${args[$i]} ]]; do
OPTARG+="${args[$i]} "
((i++))
((numargs++))
done
if [[ $(printf "%s" "$OPTARG" | rev | head -c1) == ' ' ]]; then
OPTARG=$(printf "%s" "$OPTARG" | rev | cut -c 2- | rev) # Remove the trailing space at the end of the OPTARG string
fi
fi
# Check if numargs is > $____Stringed_Short_Option_Highest_Arg_Count
if [[ $numargs -gt $____Stringed_Short_Option_Highest_Arg_Count ]]; then
____Stringed_Short_Option_Highest_Arg_Count=$numargs
fi
else
# OPT is not an allowable option as defined by the optstring
OPTARG="$OPT"
OPT='?'
fi
# If all of the options in the stringed short option have been processed, shift the number
# of arguments of the option with the highest argument count
if [[ $____Stringed_Short_Index -gt $(printf "%s" "${args[$OPTIND]}" | wc -c) ]]; then
if [[ -z $____Stringed_Short_Option_Highest_Arg_Count ]]; then
____Stringed_Short_Option_Highest_Arg_Count=0
fi
OPTIND=$(($OPTIND+$____Stringed_Short_Option_Highest_Arg_Count+1)) # shift arguments + option
unset ____Stringed_Short_Option_Highest_Arg_Count
fi
elif [[ $(printf "%s" "${args[$OPTIND]}" | head -c2) == '--' ]]; then # Long option
OPT=$(printf "%s" "${args[$OPTIND]}" | cut -c 3-) # Remove the leading double dash and set OPT as the option name
# Check that OPT is a key in opt_args
if echo "${!opt_args[@]}" | grep -qw "$OPT"; then
# Find the number of arguments that should be assosciated with this option
local numargs="${opt_args[$OPT]}"
if [[ $numargs =~ ^[1-9]+$ ]]; then # numargs is a positive integer. The option requires a specific number of args
# Add the numargs number of arguments to the OPTARG string. If to few arguments exist, return OPT as OPTARG, OPT as ':', and NUMARGS
for (( i=$((OPTIND+1)); i<=$((OPTIND+numargs)); i++ )); do
if [[ $(printf "%s" "${args["$i"]}" | head -c1) != '-' ]] && [[ -n ${args["$i"]} ]]; then
OPTARG+="${args[$i]} "
else
OPTARG="$OPT"
OPT=':'
NUMARGS="$numargs"
break
fi
done
if [[ $OPT != ':' ]]; then # The appropriate number of arguments were present
if [[ $(printf "%s" "$OPTARG" | rev | head -c1) == ' ' ]]; then
OPTARG=$(printf "%s" "$OPTARG" | rev | cut -c 2- | rev) # Remove the trailing space at the end of the OPTARG string
fi
OPTIND=$(($OPTIND+numargs+1)) # Shift the appropriate number of arguments (arguments + option)
else
((OPTIND++)) # Shift the option
while [[ $(printf "%s" "${args[$OPTIND]}" | head -c1) != '-' ]] && [[ $OPTIND -lt ${#args[@]} ]]; do # Shift until an option is found or end of arguments
((OPTIND++))
done
fi
elif [[ $numargs -eq -1 ]]; then # An unknown number of arguments are attached to this option
((OPTIND++)) # Shift the option
while [[ $(printf "%s" "${args[$OPTIND]}" | head -c1) != '-' ]] && [[ $OPTIND -lt ${#args[@]} ]]; do # While the argument is not an option and not end of arguments
OPTARG+="${args[$OPTIND]} " # Add the argument to the arguments string
((OPTIND++)) # Shift the argument
done
if [[ $(printf "%s" "$OPTARG" | rev | head -c1) == ' ' ]]; then
OPTARG=$(printf "%s" "$OPTARG" | rev | cut -c 2- | rev) # Remove the trailing space at the end of the OPTARG string
fi
else # No args are required
((OPTIND++))
fi
else
# OPT is not an allowable option as defined by the optstring
OPTARG="$OPT"
OPT='?'
((OPTIND++))
fi
elif [[ $(printf "%s" "${args[$OPTIND]}" | head -c1) == '-' ]] && [[ $(printf "%s" "${args[$OPTIND]}" | cut -c 3) != '' ]]; then # Stringed short option
OPT=$(printf "%s" "${args[$OPTIND]}" | cut -c 2) # The second character in the string is the first option to be processed
# Set the stringed short option index global variable to the third character in the string (second option)
____Stringed_Short_Index=3
# Check that OPT is a key in opt_args
if echo "${!opt_args[@]}" | grep -qw "$OPT"; then
# Find the number of arguments that should be associated with this option
local numargs="${opt_args[$OPT]}"
if [[ $numargs =~ ^[1-9]+$ ]]; then # numargs is a positive integer. The option requires a specific number of args
# Add the numargs number of arguments to the OPTARG string. If to few arguments exist, return OPT as OPTARG, OPT as ':', and NUMARGS
for (( i=$((OPTIND+1)); i<=$((OPTIND+numargs)); i++ )); do
if [[ $(printf "%s" "${args["$i"]}" | head -c1) != '-' ]] && [[ -n ${args["$i"]} ]]; then
OPTARG+="${args[$i]} "
else
OPTARG="$OPT"
OPT=':'
NUMARGS="$numargs"
break
fi
done
if [[ $OPT != ':' ]] && [[ $(printf "%s" "$OPTARG" | rev | head -c1) == ' ' ]]; then # The appropriate number of arguments were present
OPTARG=$(printf "%s" "$OPTARG" | rev | cut -c 2- | rev) # Remove the trailing space at the end of the OPTARG string
fi
elif [[ $numargs -eq -1 ]]; then # An unknown number of arguments are attached to this option
numargs=0
i=$((OPTIND+1))
while [[ $(printf "%s" "${args["$i"]}" | head -c1) != '-' ]] && [[ -n ${args["$i"]} ]]; do
OPTARG+="${args[$i]} "
((i++))
((numargs++))
done
if [[ $(printf "%s" "$OPTARG" | rev | head -c1) == ' ' ]]; then
OPTARG=$(printf "%s" "$OPTARG" | rev | cut -c 2- | rev) # Remove the trailing space at the end of the OPTARG string
fi
fi
# Assign the number of arguments for the first option to the highest number of arguments variable (____Stringed_Short_Option_Highest_Arg_Count)
____Stringed_Short_Option_Highest_Arg_Count=$numargs
else
# OPT is not an allowable option as defined by the optstring
OPTARG="$OPT"
OPT='?'
fi
else # Short option
OPT=$(printf "%s" "${args[$OPTIND]}" | cut -c 2)
# Check that OPT is a key in opt_args
if echo "${!opt_args[@]}" | grep -qw "$OPT"; then
local numargs="${opt_args[$OPT]}"
if [[ $numargs =~ ^[1-9]+$ ]]; then # numargs is a positive integer. The option requires a specific number of args
# Add the numargs number of arguments to the OPTARG string. If to few arguments exist, return OPT as OPTARG, OPT as ':', and NUMARGS
for (( i=$((OPTIND+1)); i<=$((OPTIND+numargs)); i++ )); do
if [[ $(printf "%s" "${args["$i"]}" | head -c1) != '-' ]] && [[ -n ${args["$i"]} ]]; then
OPTARG+="${args[$i]} "
else
OPTARG="$OPT"
OPT=':'
NUMARGS="$numargs"
break
fi
done
if [[ $OPT != ':' ]]; then # The appropriate number of arguments were present
if [[ $(printf "%s" "$OPTARG" | rev | head -c1) == ' ' ]]; then
OPTARG=$(printf "%s" "$OPTARG" | rev | cut -c 2- | rev) # Remove the trailing space at the end of the OPTARG string
fi
OPTIND=$(($OPTIND+numargs+1)) # Shift the appropriate number of arguments (arguments + option)
else
((OPTIND++)) # Shift the option
while [[ $(printf "%s" "${args[$OPTIND]}" | head -c1) != '-' ]] && [[ $OPTIND -lt ${#args[@]} ]]; do # Shift until an option is found or end of arguments
((OPTIND++))
done
fi
elif [[ $numargs -eq -1 ]]; then # An unknown number of arguments are attached to this option
((OPTIND++)) # Shift the option
while [[ $(printf "%s" "${args[$OPTIND]}" | head -c1) != '-' ]] && [[ $OPTIND -lt ${#args[@]} ]]; do # While the argument is not an option and not end of arguments
OPTARG+=$(printf "%s" "${args[$OPTIND]} ") # Add the argument to the arguments string
((OPTIND++)) # Shift the argument
done
if [[ $(printf "%s" "$OPTARG" | rev | head -c1) == ' ' ]]; then
OPTARG=$(printf "%s" "$OPTARG" | rev | cut -c 2- | rev) # Remove the trailing space at the end of the OPTARG string
fi
else # No args are required
((OPTIND++))
fi
else
# OPT is not an allowable option as defined by the optstring
OPTARG="$OPT"
OPT='?'
((OPTIND++))
fi
fi
fi
}