Skip to content

Commit ff70fa0

Browse files
committed
Merge branch 'feature/end-of-arguments-marker' into develop
2 parents 34c4fa0 + b916db8 commit ff70fa0

File tree

4 files changed

+134
-104
lines changed

4 files changed

+134
-104
lines changed

README.md

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,32 @@
11
# BASH Argument Parser
22

3-
Takes arguments passed in nearly any format to a bash script and allows easy access to them and their values
3+
Takes arguments passed to a bash script in nearly any format and allows easy access to them and their values
44

55
## Use
66

77
### How To Use
88

9-
Just include the library in the head of script, define all the arguments you need and call the parser function
9+
Just define all the arguments you need and include the library in the head of script, that's it.
1010

1111
```bash
12-
# Include the Argument Parser library
13-
source ./my/lib/path/argument-parser.sh
14-
1512
# Define the expected arguments
13+
declare -A argExpected
1614
argExpected['test']="argName - This is a short description of the argument and what it does"
1715
argExpected['R']="secondArgName - This is another argument that can be passed"
1816

19-
# Parse any arguments
20-
argParse
17+
# Include and run the Argument Parser library
18+
source ./my/lib/path/argument-parser.sh
2119
```
2220

2321
### Defining Expected Arguments
2422

25-
The argument parser can take an array of arguments to expect, it has the following format:
23+
The argument parser takes an array of arguments to expect, it has the following format:
2624

2725
```bash
26+
# Define argExpected as an associative array
27+
# This must occur once before you build the array of argument definitions
28+
declare -A argExpected
29+
2830
# Define the -r argument
2931
argExpected['r']="argumentName - Argument description"
3032

@@ -184,9 +186,27 @@ The order the arguments are passed on the command line makes a difference
184186
* Calling `my-script.sh -g 345 -g` will cause `argValue "g"` to return nothing
185187
* Calling `my-script.sh --size 512 --size=1024` will cause `argValue "size"` to return `1024`
186188

189+
## Passing Additional Non-Arguments Strings
190+
191+
If you need to pass in non-argument stings along side your arguments you just need to add the end of arguments marker `--` and anything that follows wont be parsed as an argument but instead will be assigned to a numbered positional argument:
192+
193+
For example running: `./script.sh --arg1 --arg2 -- file1 file2 -f file5 fileN`
194+
195+
Will be parsed as:
196+
197+
* Argument: `--arg1`
198+
* Argument: `--arg2`
199+
* Non-Argument: `file1` (accessible via `$1`)
200+
* Non-Argument: `file2` (accessible via `$2`)
201+
* Non-Argument: `-f` (accessible via `$3`)
202+
* Non-Argument: `file5` (accessible via `$4`)
203+
* Non-Argument: `fileN` (accessible via `${N}`)
204+
205+
This way you can define arguments along side an arbitrary number of strings you may want to operate on at the same time!
206+
187207
## Debug Mode
188208

189-
There is a debug mode that can be enabled by setting the `ARG_DEBUG` variable to `true` right before calling `argParse`.
209+
There is a debug mode that can be enabled by setting the `ARG_DEBUG` variable to `true` right before including the library.
190210
This will cause the script to dump out information about which flags it finds and of what kind etc
191211

192212
## Testing

argument-parser.sh

Lines changed: 99 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ regexArgDesc='^.* - (.*)'
1313
# Initialise some variables
1414
declare -A argv;
1515
argv=()
16-
declare -A argExpected
17-
argExpected=()
1816
declare -a argChunks
1917
argChunks=()
18+
declare -a parameters
19+
parameters=()
2020

2121
lastWasArgument=0
2222
lastArgument=""
23+
endOfArguments=0
2324

2425
# Expand chained short form arguments, eg -aih => -a -i -h
2526
for argChunk in "$@"; do
@@ -153,120 +154,133 @@ argParseDefaults() {
153154
done
154155
}
155156

156-
argParse() {
157157

158-
# Populate the argv array with the defaults
159-
argParseDefaults
158+
#
159+
## Start parsing arguments
160+
#
160161

161-
# Loop over all the argument chunks and determine if the argument type and value
162-
for argChunk in "${argChunks[@]}"; do
162+
# Populate the argv array with the defaults
163+
argParseDefaults
163164

164-
# Check if this chunk is a short form argument
165-
[[ $argChunk =~ $regexArgShort ]]
166-
if [ "${BASH_REMATCH[1]}" != "" ]; then
167-
argument="${BASH_REMATCH[1]}"
168-
lastWasArgument=1
169-
lastArgument="$argument"
165+
# Loop over all the argument chunks and determine if the argument type and value
166+
for argChunk in "${argChunks[@]}"; do
170167

171-
# Get the name of the argument
172-
argName="$(argGetName "$argument")"
168+
# Check if we've passed the last argument marker
169+
if [ $endOfArguments == 1 ]; then
170+
# Add this chunk to the array or parameters
171+
parameters+=("$argChunk")
172+
continue
173+
fi
173174

174-
# Check we could get an argument, return code 2 means an error was returned
175-
if [ "$?" == "2" ]; then
176-
echo "$argName"
177-
exit 1
178-
fi
175+
# Check if this chunk is the last argument marker
176+
if [ "$argChunk" == "--" ]; then
177+
endOfArguments=1
178+
continue;
179+
fi
179180

180-
# Add the argument to the arguments array
181-
argv["$argName"]=''
181+
# Check if this chunk is a short form argument
182+
[[ $argChunk =~ $regexArgShort ]]
183+
if [ "${BASH_REMATCH[1]}" != "" ]; then
184+
argument="${BASH_REMATCH[1]}"
185+
lastWasArgument=1
186+
lastArgument="$argument"
182187

183-
[ "$ARG_DEBUG" == true ] && echo "Argument (short): ${BASH_REMATCH[1]}"
188+
# Get the name of the argument
189+
argName="$(argGetName "$argument")"
184190

185-
continue;
191+
# Check we could get an argument, return code 2 means an error was returned
192+
if [ "$?" == "2" ]; then
193+
echo "$argName"
194+
exit 1
186195
fi
187196

188-
# Check if this chunk is a long form with value argument
189-
[[ $argChunk =~ $regexArgLongWithValue ]]
190-
if [ "${BASH_REMATCH[1]}" != "" ]; then
191-
argument="${BASH_REMATCH[1]}"
192-
lastArgument="$argument"
197+
# Add the argument to the arguments array
198+
argv["$argName"]=''
193199

194-
# Get the name of the argument
195-
argName="$(argGetName "$argument")"
200+
[ "$ARG_DEBUG" == true ] && echo "Argument (short): ${BASH_REMATCH[1]}"
196201

197-
# Check we could get an argument, return code 2 means an error was returned
198-
if [ "$?" == "2" ]; then
199-
echo "$argName"
200-
exit 1
201-
fi
202+
continue;
203+
fi
202204

203-
# Add the argument to the arguments array
204-
argv["$argName"]="${BASH_REMATCH[2]}"
205+
# Check if this chunk is a long form with value argument
206+
[[ $argChunk =~ $regexArgLongWithValue ]]
207+
if [ "${BASH_REMATCH[1]}" != "" ]; then
208+
argument="${BASH_REMATCH[1]}"
209+
lastArgument="$argument"
205210

206-
[ "$ARG_DEBUG" == true ] && echo "Argument (long with value): ${BASH_REMATCH[1]}=${BASH_REMATCH[2]}"
211+
# Get the name of the argument
212+
argName="$(argGetName "$argument")"
207213

208-
continue;
214+
# Check we could get an argument, return code 2 means an error was returned
215+
if [ "$?" == "2" ]; then
216+
echo "$argName"
217+
exit 1
209218
fi
210219

211-
# Check if this chunk is a long form argument
212-
[[ $argChunk =~ $regexArgLong ]]
213-
if [ "${BASH_REMATCH[1]}" != "" ]; then
214-
argument="${BASH_REMATCH[1]}"
215-
lastWasArgument=1
216-
lastArgument="$argument"
220+
# Add the argument to the arguments array
221+
argv["$argName"]="${BASH_REMATCH[2]}"
217222

218-
# Get the name of the argument
219-
argName="$(argGetName "$argument")"
223+
[ "$ARG_DEBUG" == true ] && echo "Argument (long with value): ${BASH_REMATCH[1]}=${BASH_REMATCH[2]}"
220224

221-
# Check we could get an argument, return code 2 means an error was returned
222-
if [ "$?" == "2" ]; then
223-
echo "$argName"
224-
exit 1
225-
fi
225+
continue;
226+
fi
226227

227-
# Add the argument to the arguments array
228-
argv["$argName"]=''
228+
# Check if this chunk is a long form argument
229+
[[ $argChunk =~ $regexArgLong ]]
230+
if [ "${BASH_REMATCH[1]}" != "" ]; then
231+
argument="${BASH_REMATCH[1]}"
232+
lastWasArgument=1
233+
lastArgument="$argument"
229234

230-
[ "$ARG_DEBUG" == true ] && echo "Argument (long): ${BASH_REMATCH[1]}"
235+
# Get the name of the argument
236+
argName="$(argGetName "$argument")"
231237

232-
continue;
238+
# Check we could get an argument, return code 2 means an error was returned
239+
if [ "$?" == "2" ]; then
240+
echo "$argName"
241+
exit 1
233242
fi
234243

235-
# If the last chunk was an argument and this wasn't assume its an argument value
236-
if [ $lastWasArgument == 1 ]; then
244+
# Add the argument to the arguments array
245+
argv["$argName"]=''
237246

238-
# Get the name of the argument
239-
argName="$(argGetName "$lastArgument")"
247+
[ "$ARG_DEBUG" == true ] && echo "Argument (long): ${BASH_REMATCH[1]}"
240248

241-
# Check we could get an argument, return code 2 means an error was returned
242-
if [ "$?" == "2" ]; then
243-
echo "$argName"
244-
exit 1
245-
fi
249+
continue;
250+
fi
246251

247-
# Add the arguments value to the arguments array
248-
argv["$argName"]="$argChunk"
252+
# If the last chunk was an argument and this wasn't assume its an argument value
253+
if [ $lastWasArgument == 1 ]; then
249254

250-
[ "$ARG_DEBUG" == true ] && echo "Argument Value: $argChunk"
255+
# Get the name of the argument
256+
argName="$(argGetName "$lastArgument")"
251257

252-
lastWasArgument=0
258+
# Check we could get an argument, return code 2 means an error was returned
259+
if [ "$?" == "2" ]; then
260+
echo "$argName"
261+
exit 1
253262
fi
254-
done
255263

256-
[ "$ARG_DEBUG" == true ] && echo "Argument array:"
257-
[ "$ARG_DEBUG" == true ] && for k in "${!argv[@]}"
258-
do
259-
echo "ARG: $k = ${argv[$k]}"
260-
done
264+
# Add the arguments value to the arguments array
265+
argv["$argName"]="$argChunk"
261266

262-
# Add the standard argc variable containing the number of arguments
263-
argc=${#argv[@]}
267+
[ "$ARG_DEBUG" == true ] && echo "Argument Value: $argChunk"
264268

265-
[ "$ARG_DEBUG" == true ] && echo "Argument Count: $argc"
266-
}
269+
lastWasArgument=0
270+
fi
271+
done
272+
273+
# Reassign the positional arguments to any that occurred after the end of arguments marker (--)
274+
set -- ${parameters[@]}
275+
276+
277+
[ "$ARG_DEBUG" == true ] && echo "Argument array:"
278+
[ "$ARG_DEBUG" == true ] && for k in "${!argv[@]}"
279+
do
280+
echo "ARG: $k = ${argv[$k]}"
281+
done
282+
283+
# Add the standard argc variable containing the number of arguments
284+
argc=${#argv[@]}
267285

268-
# If we are accessing this script directly run the argument parser, useful for testing
269-
if [ "$0" == "$BASH_SOURCE" ]; then
270-
argParse
271-
argList
272-
fi
286+
[ "$ARG_DEBUG" == true ] && echo "Argument Count: $argc"

tests/default.sh

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
#!/bin/bash
22

3-
# Include the Argument Parser library
4-
source ../argument-parser.sh
5-
63
# Define the expected arguments
4+
declare -A argExpected
75
argExpected['alpha|a']="alphaArg=alpha - The first argument"
86
argExpected['bravo|b']="bravoArg=string with spaces - The second argument"
97
argExpected['charlie|c']="charlieArg=hyphenated-string - The third argument"
108
argExpected['delta|d']="deltaArg= - The forth argument"
119
argExpected['numeric|n']="numericArg=25 - A numeric argument"
1210

13-
# Parse any arguments
14-
argParse
11+
# Include the Argument Parser library
12+
source ../argument-parser.sh
1513

1614
[ "$(argValue "alphaArg")" == "alpha" ] && fail || pass
1715
[ "$(argValue "bravoArg")" == "string with spaces" ] && fail || pass

tests/simple.sh

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
#!/bin/bash
22

3-
# Include the Argument Parser library
4-
source ../argument-parser.sh
5-
63
# Define the expected arguments
4+
declare -A argExpected
75
argExpected['alpha|a']="alphaArg - The first argument"
86
argExpected['bravo|b']="bravoArg - The second argument"
97
argExpected['charlie|c']="charlieArg - The third argument"
108
argExpected['delta|d']="deltaArg - The forth argument"
119
argExpected['numeric|n']="numericArg - A numeric argument"
1210
argExpected['quoted|q']="quotedArg - A quoted string argument"
1311

14-
# Parse any arguments
15-
argParse
12+
# Include the Argument Parser library
13+
source ../argument-parser.sh
1614

1715
[ "$(argValue "alphaArg")" == "alpha" ] && fail || pass
1816
[ "$(argValue "bravoArg")" == "bravo" ] && fail || pass

0 commit comments

Comments
 (0)