Added long opt parsing, now understands -- and parses non-option elements into cmdarg_argv. Docs updated.
This commit is contained in:
54
README.md
54
README.md
@@ -26,7 +26,14 @@ This function is used to tell the library what command line arguments you accept
|
|||||||
cmdarg 'u:' 'source_ldap_username' 'Source (old) LDAP Username'
|
cmdarg 'u:' 'source_ldap_username' 'Source (old) LDAP Username'
|
||||||
cmdarg 'c:' 'groupmap' 'A CSV file mapping usernames to groups that they should belong to post-conversion' '' 'test -e $OPTARG'
|
cmdarg 'c:' 'groupmap' 'A CSV file mapping usernames to groups that they should belong to post-conversion' '' 'test -e $OPTARG'
|
||||||
|
|
||||||
All arguments are OPTIONAL by default. An argument that has ':' on the end of its single character option, and does not specify a default value (empty string is considered "not specified"), is REQUIRED.
|
All arguments are OPTIONAL by default. An argument that has ':' on the end of its single character option, and does not specify a default value (empty string is considered "not specified"), is REQUIRED. The arguments can be set on the command line either via '-X' or '--Y', where X is the short option and Y is the long option. Example:
|
||||||
|
|
||||||
|
cmdarg 'r:' 'required-thing' 'Some thing I require'
|
||||||
|
|
||||||
|
# your_script.sh -r some_thingy
|
||||||
|
# your_script.sh --required-thing some_thingy
|
||||||
|
|
||||||
|
Because cmdarg does key off of the short options, you are limited to as many unique single characters are in your character set (likely 61 - 26 lower & upper alpha, +9 numerics).
|
||||||
|
|
||||||
cmdarg_info
|
cmdarg_info
|
||||||
===========
|
===========
|
||||||
@@ -46,7 +53,7 @@ This command does what you expect, parsing your command line arguments. However
|
|||||||
|
|
||||||
... Beware that "$@" will change depending on your context. So if you have a main() function called in your script, you need to make sure that you pass "$@" from the toplevel script in to it, otherwise the options will be blank when you pass them to cmdarg_parse.
|
... Beware that "$@" will change depending on your context. So if you have a main() function called in your script, you need to make sure that you pass "$@" from the toplevel script in to it, otherwise the options will be blank when you pass them to cmdarg_parse.
|
||||||
|
|
||||||
Any argument parsed that has a validator assigned, and whose validator returns nonzero, is considered a failure. Any REQUIRED argument that is not specified is considered a failure.
|
Any argument parsed that has a validator assigned, and whose validator returns nonzero, is considered a failure. Any REQUIRED argument that is not specified is considered a failure. However, it is worth noting that if a required argument has a default value, and you provide an empty value to it, we won't know any better and that will be accepted (how do we know you didn't actually *mean* to do that?).
|
||||||
|
|
||||||
For every argument, a global associative array "cmdarg_cfg" is populated with the long version of the option. E.g., in the example above, '-c' would become ${cmdarg_cfg['groupmap']}, for friendlier access during scripting.
|
For every argument, a global associative array "cmdarg_cfg" is populated with the long version of the option. E.g., in the example above, '-c' would become ${cmdarg_cfg['groupmap']}, for friendlier access during scripting.
|
||||||
|
|
||||||
@@ -153,4 +160,45 @@ Given some code like this:
|
|||||||
cmdarg_cfg[source_ldap_basedn]="1"
|
cmdarg_cfg[source_ldap_basedn]="1"
|
||||||
cmdarg_cfg[source_ldap_ou_users]="users"
|
cmdarg_cfg[source_ldap_ou_users]="users"
|
||||||
cmdarg_cfg[source_ldap]="1"
|
cmdarg_cfg[source_ldap]="1"
|
||||||
cmdarg_cfg[dest_ldap]="1"
|
cmdarg_cfg[dest_ldap]="1"
|
||||||
|
|
||||||
|
Setting arrays and hashes
|
||||||
|
=========================
|
||||||
|
|
||||||
|
You can use the cmdarg function to accept arrays and hashes from the command line as well. Consider:
|
||||||
|
|
||||||
|
declare -a array
|
||||||
|
declare -A hash
|
||||||
|
cmdarg 'a:[]' 'array' 'Some array you can set indexes in'
|
||||||
|
cmdarg 'H:{}' 'hash' 'Some hash you can set keys in'
|
||||||
|
|
||||||
|
|
||||||
|
your_script -a 32 --array something -H key=value --hash other_key=value
|
||||||
|
|
||||||
|
|
||||||
|
echo ${array[0]}
|
||||||
|
echo ${array[1]}
|
||||||
|
echo ${hash['key']}
|
||||||
|
echo ${hash['other_key']}
|
||||||
|
|
||||||
|
The long option names in this form must equal the name of a previously declared array or hash, appropriately. Cmdarg populates that variable directly with options for these arguments.
|
||||||
|
|
||||||
|
Positional arguments and --
|
||||||
|
===========================
|
||||||
|
|
||||||
|
Like any good option parsing framework, cmdarg understands '--' and positional arguments that are meant to be provided without any kind of option parsing applied to them. So if you have:
|
||||||
|
|
||||||
|
myscript.sh -x 0 --longopt thingy file1 file2
|
||||||
|
|
||||||
|
... It would seem reasonable to assume that -x and --longopt would be parsed as expected; with arguments of 0 and thingy. But what to do with file1 and file2? cmdarg puts those into a bash indexed array called cmdarg_argv.
|
||||||
|
|
||||||
|
Similarly, cmdarg understands '--' which means "stop processing arguments, the rest of this stuff is just to be passed to the program directly". So in this case:
|
||||||
|
|
||||||
|
myscript.sh -x 0 --longopt thingy -- --some-thing-with-dashes
|
||||||
|
|
||||||
|
... Cmdarg would parse -x and --longopt as expected, and then ${cmdarg_argv[0]} would hold "--some-thing-with-dashes", for your program to do with what it will.
|
||||||
|
|
||||||
|
Tests
|
||||||
|
=====
|
||||||
|
|
||||||
|
cmdarg is testable by the shunit bash unit testing tool. See the tests/ directory.
|
||||||
|
|||||||
41
cmdarg.sh
41
cmdarg.sh
@@ -161,7 +161,7 @@ function cmdarg_set_opt
|
|||||||
arg="$2"
|
arg="$2"
|
||||||
case ${CMDARG_TYPES[$key]} in
|
case ${CMDARG_TYPES[$key]} in
|
||||||
$CMDARG_TYPE_STRING)
|
$CMDARG_TYPE_STRING)
|
||||||
cmdarg_cfg[$key]=$OPTARG
|
cmdarg_cfg[$key]=$arg
|
||||||
;;
|
;;
|
||||||
$CMDARG_TYPE_BOOLEAN)
|
$CMDARG_TYPE_BOOLEAN)
|
||||||
cmdarg_cfg[$key]=true
|
cmdarg_cfg[$key]=true
|
||||||
@@ -225,19 +225,46 @@ function cmdarg_parse
|
|||||||
#
|
#
|
||||||
# Call it EXACTLY LIKE THAT, and it will parse your arguments for you.
|
# Call it EXACTLY LIKE THAT, and it will parse your arguments for you.
|
||||||
# This function only knows about the arguments that you previously called 'cmdarg' for.
|
# This function only knows about the arguments that you previously called 'cmdarg' for.
|
||||||
local OPTIND
|
local OPTIND parsing fullopt opt optarg longopt
|
||||||
|
|
||||||
|
parsing=0
|
||||||
|
while [[ "$@" != "" ]]; do
|
||||||
|
optarg=""
|
||||||
|
opt=""
|
||||||
|
longopt=""
|
||||||
|
fullopt=$1
|
||||||
|
shift
|
||||||
|
if [[ "$fullopt" == "--" ]] && [[ $parsing -eq 0 ]]; then
|
||||||
|
cmdarg_argv+=($@)
|
||||||
|
break
|
||||||
|
elif [[ "${fullopt:0:2}" == "--" ]]; then
|
||||||
|
longopt=${fullopt:2}
|
||||||
|
opt=${CMDARG_REV[$longopt]}
|
||||||
|
elif [[ "${fullopt:0:1}" == "-" ]] && [[ ${#fullopt} -eq 2 ]]; then
|
||||||
|
opt=${fullopt:1}
|
||||||
|
longopt=${CMDARG[$opt]}
|
||||||
|
else
|
||||||
|
echo "Malformed argument: ${fullopt}" >&2
|
||||||
|
echo "While parsing: $@" >&2
|
||||||
|
cmdarg_usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${CMDARG_FLAGS[$opt]} -eq $CMDARG_FLAG_WITHARG ]]; then
|
||||||
|
optarg=$1
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
while getopts "$CMDARG_GETOPTLIST" opt "$@"; do
|
|
||||||
if [ "$opt" == "h" ]; then
|
if [ "$opt" == "h" ]; then
|
||||||
cmdarg_usage
|
cmdarg_usage
|
||||||
exit 1
|
exit 1
|
||||||
elif [ ${CMDARG["${opt}"]+abc} ]; then
|
elif [ ${CMDARG["${opt}"]+abc} ]; then
|
||||||
cmdarg_set_opt "${CMDARG[$opt]}" "$OPTARG"
|
cmdarg_set_opt "${CMDARG[$opt]}" "$optarg"
|
||||||
else
|
else
|
||||||
|
echo "Unknown argument or invalid value : -${opt} | --${longopt}" >&2
|
||||||
cmdarg_usage
|
cmdarg_usage
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
OPTARG=""
|
|
||||||
done
|
done
|
||||||
|
|
||||||
# --- Don't exit early during validation, tell the user
|
# --- Don't exit early during validation, tell the user
|
||||||
@@ -254,7 +281,7 @@ function cmdarg_parse
|
|||||||
done
|
done
|
||||||
|
|
||||||
local opt
|
local opt
|
||||||
local optarg
|
local OPTARG
|
||||||
for opt in "${!cmdarg_cfg[@]}"
|
for opt in "${!cmdarg_cfg[@]}"
|
||||||
do
|
do
|
||||||
shortopt=${CMDARG_REV[$opt]}
|
shortopt=${CMDARG_REV[$opt]}
|
||||||
@@ -351,4 +378,6 @@ declare -xA CMDARG_INFO
|
|||||||
declare -xA CMDARG_FLAGS
|
declare -xA CMDARG_FLAGS
|
||||||
# Map of (short arg) -> type (string, array, hash)
|
# Map of (short arg) -> type (string, array, hash)
|
||||||
declare -xA CMDARG_TYPES
|
declare -xA CMDARG_TYPES
|
||||||
|
# Array of all elements found after --
|
||||||
|
declare -xa cmdarg_argv
|
||||||
CMDARG_GETOPTLIST="h"
|
CMDARG_GETOPTLIST="h"
|
||||||
|
|||||||
37
tests/test_dashdash.sh
Normal file
37
tests/test_dashdash.sh
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/bash4
|
||||||
|
|
||||||
|
source $(dirname ${BASH_SOURCE[0]})/../cmdarg.sh
|
||||||
|
|
||||||
|
function shunittest_dashdash
|
||||||
|
{
|
||||||
|
set -x
|
||||||
|
cmdarg_purge
|
||||||
|
cmdarg_parse -- lolzors something
|
||||||
|
[[ "${cmdarg_argv[0]}" == "lolzors" ]] || return 1
|
||||||
|
[[ "${cmdarg_argv[1]}" == "something" ]] || return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function shunittest_missing_dashdash
|
||||||
|
{
|
||||||
|
set -x
|
||||||
|
cmdarg_purge
|
||||||
|
( cmdarg_parse --lolzors ) || return 0
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function shunittest_withbool_missing_dashdash
|
||||||
|
{
|
||||||
|
set -x
|
||||||
|
cmdarg_purge
|
||||||
|
cmdarg 'x' 'xray' 'thingy for xray'
|
||||||
|
( cmdarg_parse -x lolzors ) || return 0
|
||||||
|
cmdarg_parse -x -- lolzors
|
||||||
|
}
|
||||||
|
|
||||||
|
function shunittest_withopt_with_dashdash
|
||||||
|
{
|
||||||
|
set -x
|
||||||
|
cmdarg_purge
|
||||||
|
cmdarg 'x:' 'xray' 'thingy for xray'
|
||||||
|
( cmdarg_parse -x -- lolzors ) || return 0
|
||||||
|
}
|
||||||
31
tests/test_longopt.sh
Normal file
31
tests/test_longopt.sh
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/bash4
|
||||||
|
|
||||||
|
source $(dirname ${BASH_SOURCE[0]})/../cmdarg.sh
|
||||||
|
|
||||||
|
function shunittest_longopt
|
||||||
|
{
|
||||||
|
cmdarg_purge
|
||||||
|
cmdarg 'l:' 'long-required-opt' 'Some long opt that requires a value'
|
||||||
|
cmdarg 'o' 'long-boolean-opt' 'Some long option that is boolean'
|
||||||
|
cmdarg 'L:' 'long-required-default-opt' 'Some long opt that requires a value but has a default' '(nil)'
|
||||||
|
|
||||||
|
cmdarg_parse --long-required-opt hooha --long-boolean-opt
|
||||||
|
|
||||||
|
[[ "${cmdarg_cfg['long-required-opt']}" == "hooha" ]] || return 1
|
||||||
|
[[ "${cmdarg_cfg['long-boolean-opt']}" == "true" ]] || return 1
|
||||||
|
[[ "${cmdarg_cfg['long-required-default-opt']}" == "(nil)" ]] || return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function shunittest_longopt_shortopts_still_work
|
||||||
|
{
|
||||||
|
cmdarg_purge
|
||||||
|
cmdarg 'l:' 'long-required-opt' 'Some long opt that requires a value'
|
||||||
|
cmdarg 'o' 'long-boolean-opt' 'Some long option that is boolean'
|
||||||
|
cmdarg 'L:' 'long-required-default-opt' 'Some long opt that requires a value but has a default' '(nil)'
|
||||||
|
|
||||||
|
cmdarg_parse -l hooha -o
|
||||||
|
|
||||||
|
[[ "${cmdarg_cfg['long-required-opt']}" == "hooha" ]] || return 1
|
||||||
|
[[ "${cmdarg_cfg['long-boolean-opt']}" == "true" ]] || return 1
|
||||||
|
[[ "${cmdarg_cfg['long-required-default-opt']}" == "(nil)" ]] || return 1
|
||||||
|
}
|
||||||
@@ -64,7 +64,7 @@ function shunittest_hash_values
|
|||||||
do
|
do
|
||||||
cmp="$cmp ${k}=${hash[$k]}"
|
cmp="$cmp ${k}=${hash[$k]}"
|
||||||
done
|
done
|
||||||
cmp=$(echo "$cmp" | sed s/'^\s*'//)
|
cmp=$(echo "$cmp" | sed s/'^ *'//)
|
||||||
if [[ "$cmp" != "$base" ]]; then
|
if [[ "$cmp" != "$base" ]]; then
|
||||||
echo "Hash does not contain expected arguments ($cmp vs $base)"
|
echo "Hash does not contain expected arguments ($cmp vs $base)"
|
||||||
cmdarg_dump >&2
|
cmdarg_dump >&2
|
||||||
|
|||||||
Reference in New Issue
Block a user