227 lines
11 KiB
Markdown
227 lines
11 KiB
Markdown
cmdarg
|
|
======
|
|
|
|
[](http://jenkins.aklabs.net/job/cmdarg-test/)
|
|
|
|
Requires bash >= 4.
|
|
|
|
source cmdarg.sh
|
|
|
|
Enjoy
|
|
|
|
Usage
|
|
=====
|
|
|
|
cmdarg is a helper library I wrote for bash scripts because, at current, option parsing in bash (-foo bar, etc) is really hard, lots harder than it should be, given bash's target audience. So here's my solution. There are 4 functions you will care about:
|
|
|
|
cmdarg
|
|
cmdarg_info
|
|
cmdarg_parse
|
|
cmdarg_usage
|
|
|
|
cmdarg
|
|
======
|
|
|
|
This function is used to tell the library what command line arguments you accept. Check cmdarg.sh for the latest syntax.
|
|
|
|
cmdarg 'l:' 'source_ldap' 'Source (old) LDAP URI'
|
|
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'
|
|
|
|
The first argument to cmdarg must be an argument specification. Argument specifications take the form 'NOT', where:
|
|
|
|
- N : The single letter Name of the argument
|
|
- O : Whether the option is optional or not. Use ':' here for a required argument, '?' for an optional argument. If you provide a default value for a required argument (:), then it becomes optional.
|
|
- T : The type. Leave empty for a string argument, use '[]' for an array argument, use '{}' for a hash argument.
|
|
|
|
If O and T are both unset, and only the single letter N is provided, then the argument is a boolean argument which will default to false.
|
|
|
|
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'
|
|
cmdarg 'o?' 'optional-thing' 'Some optional thing'
|
|
cmdarg 'b' 'boolean-thing' 'Some boolean thing'
|
|
|
|
# your_script.sh -r some_thingy -b -o optional_thing
|
|
# your_script.sh --required-thing some_thingy --boolean-thing
|
|
|
|
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
|
|
===========
|
|
|
|
This function sets up information about your program for use when printing the help/usage message. Again, see cmdarg.sh for the latest syntax.
|
|
|
|
cmdarg_info "header" "Some script that needed argument parsing"
|
|
cmdarg_info "author" "Some Poor Bastard <somepoorbastard@hell.com>"
|
|
cmdarg_info "copyright" "(C) 2013"
|
|
|
|
cmdarg_parse
|
|
============
|
|
|
|
This command does what you expect, parsing your command line arguments. However you must pass your command line arguments to it. Generally this means:
|
|
|
|
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. 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 integer, boolean or string 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 array and hash arguments, you must declare the hash or array beforehand for population:
|
|
|
|
declare -a myarray
|
|
cmdarg 'a?[]' 'myarray' 'Some array of stuff'
|
|
cmdarg_parse "$@"
|
|
# Now you will be able to access ${myarray[0]}, ${myarray[1]}, etc. Similarly with hashes, just use declare -A and {}.
|
|
|
|
I love it when a plan comes together
|
|
====================================
|
|
|
|
Given some code like this:
|
|
|
|
cmdarg_info "header" "Convert existing old LDAP users to the new LDAP server/schema."
|
|
cmdarg_info "author" "Some Poor Bastard <somepoorbastard@hell.com>"
|
|
cmdarg_info "copyright" "(C) 2013"
|
|
|
|
cmdarg 'C:' 'cfgfile' 'Config file that contains options that should be used in place of command line args' '' 'test -e $OPTARG'
|
|
cmdarg 'c:' 'groupmap' 'A CSV file mapping usernames to groups that they should belong to post-conversion' '' 'test -e $OPTARG'
|
|
cmdarg 'l:' 'source_ldap' 'Source (old) LDAP URI'
|
|
cmdarg 'u:' 'source_ldap_username' 'Source (old) LDAP Username'
|
|
cmdarg 'p:' 'source_ldap_password' 'Source (old) LDAP Password'
|
|
cmdarg 'b:' 'source_ldap_basedn' 'Source (old) LDAP Base DN (ou=x,dc=x,dc=x)'
|
|
cmdarg 'o:' 'source_ldap_ou_users' 'Source (old) LDAP ou for Users' 'users'
|
|
cmdarg 'g:' 'source_ldap_ou_groups' 'Source (old) LDAP ou for Groups' 'groups'
|
|
cmdarg 'L:' 'dest_ldap' 'Destination (new) LDAP URI'
|
|
cmdarg 'U:' 'dest_ldap_username' 'Destination (new) LDAP Username'
|
|
cmdarg 'P:' 'dest_ldap_password' 'Destination (new) LDAP Password'
|
|
cmdarg 'B:' 'dest_ldap_basedn' 'Destination (new) LDAP Base DN (dc=x,dc=x)'
|
|
cmdarg 'O:' 'source_ldap_ou_users' 'Destination (new) LDAP ou for Users' 'users'
|
|
cmdarg 'G:' 'source_ldap_ou_groups' 'Destination (new) LDAP ou for Groups' 'groups'
|
|
cmdarg 's:' 'slappasswd_salt' 'Slappasswd salt format (man slappasswd)' 'rofflewaffles%s'
|
|
cmdarg 'S:' 'slappasswd_scheme' 'Slappasswwd hash scheme to use (CRYPT|MD5|SMD5|SSHA|SHA)' 'SSHA' 'echo $OPTARG | grep -E "CRYPT|MD5|SMD5|SSHA|SHA" >/dev/null 2>&1'
|
|
|
|
cmdarg_parse "$@"
|
|
|
|
... Here's what we can expect to see from the usage message:
|
|
|
|
$ ./ldap-convert.sh -h
|
|
ldap-convert.sh (C) 2013 : Some Poor Bastard <somepoorbastard@hell.com>
|
|
|
|
Convert existing LDAP users to the new LDAP server/schema.
|
|
|
|
Required Arguments:
|
|
-C : Config file that contains options that should be used in place of command line args
|
|
-c : A CSV file mapping usernames to groups that they should belong to post-conversion
|
|
-l : Source (old) LDAP URI
|
|
-u : Source (old) LDAP Username
|
|
-p : Source (old) LDAP Password
|
|
-b : Source (old) LDAP Base DN (ou=x,dc=x,dc=x)
|
|
-L : Destination (new) LDAP URI
|
|
-U : Destination (new) LDAP Username
|
|
-P : Destination (new) LDAP Password
|
|
-B : Destination (new) LDAP Base DN (dc=x,dc=x)
|
|
|
|
Optional Arguments:
|
|
-o : Source (old) LDAP ou for Users (Default "users")
|
|
-g : Source (old) LDAP ou for Groups (Default "groups")
|
|
-O : Destination (new) LDAP ou for Users (Default "users")
|
|
-G : Destination (new) LDAP ou for Groups (Default "groups")
|
|
-s : Slappasswd salt format (man slappasswd) (Default "rofflewaffles%s")
|
|
-S : Slappasswwd hash scheme to use (CRYPT|MD5|SMD5|SSHA|SHA) (Default "SSHA")
|
|
|
|
... And if we run it without '-h', then the argument parser (rather helpfully) tells us which arguments we've failed to specify, before printing the help:
|
|
|
|
$ ./ldap-convert.sh
|
|
Invalid value for -c :
|
|
Invalid value for -C :
|
|
Missing arguments : -C -c -l -u -p -b -L -U -P -B
|
|
|
|
ldap-convert.sh (C) 2013 : Some Poor Bastard <somepoorbastard@hell.com>
|
|
|
|
Convert existing LDAP users to the new LDAP server/schema.
|
|
|
|
Required Arguments:
|
|
-C : Config file that contains options that should be used in place of command line args
|
|
-c : A CSV file mapping usernames to groups that they should belong to post-conversion
|
|
-l : Source (old) LDAP URI
|
|
-u : Source (old) LDAP Username
|
|
-p : Source (old) LDAP Password
|
|
-b : Source (old) LDAP Base DN (ou=x,dc=x,dc=x)
|
|
-L : Destination (new) LDAP URI
|
|
-U : Destination (new) LDAP Username
|
|
-P : Destination (new) LDAP Password
|
|
-B : Destination (new) LDAP Base DN (dc=x,dc=x)
|
|
|
|
Optional Arguments:
|
|
-o : Source (old) LDAP ou for Users (Default "users")
|
|
-g : Source (old) LDAP ou for Groups (Default "groups")
|
|
-O : Destination (new) LDAP ou for Users (Default "users")
|
|
-G : Destination (new) LDAP ou for Groups (Default "groups")
|
|
-s : Slappasswd salt format (man slappasswd) (Default "rofflewaffles%s")
|
|
-S : Slappasswwd hash scheme to use (CRYPT|MD5|SMD5|SSHA|SHA) (Default "SSHA")
|
|
|
|
... And here's what the cmdarg_cfg associate array winds up holding, illustrated with some debug prints:
|
|
|
|
$ ./ldap-convert.sh -c ./users_groups_map.csv -l 1 -u 1 -p 1 -b 1 -L 1 -U 1 -P 1 -B 1
|
|
|
|
cmdarg_cfg[groupmap]="./users_groups_map.csv"
|
|
cmdarg_cfg[slappasswd_salt]="rofflewaffles%s"
|
|
cmdarg_cfg[source_ldap_ou_groups]="groups"
|
|
cmdarg_cfg[slappasswd_scheme]="SSHA"
|
|
cmdarg_cfg[dest_ldap_username]="1"
|
|
cmdarg_cfg[dest_ldap_password]="1"
|
|
cmdarg_cfg[source_ldap_password]="1"
|
|
cmdarg_cfg[source_ldap_username]="1"
|
|
cmdarg_cfg[cfgfile]="/dev/null"
|
|
cmdarg_cfg[dest_ldap_basedn]="1"
|
|
cmdarg_cfg[source_ldap_basedn]="1"
|
|
cmdarg_cfg[source_ldap_ou_users]="users"
|
|
cmdarg_cfg[source_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.
|
|
|
|
getopt vs getopts
|
|
=================
|
|
|
|
cmdarg does not use getopt or getopts for option parsing. Its parser is written in 100% pure bash, and is self contained in cmdarg_parse. It will run the same way anywhere you have bash4.
|
|
|
|
Tests
|
|
=====
|
|
|
|
cmdarg is testable by the shunit bash unit testing tool (https://www.github.com/akesterson/shunit/). See the tests/ directory.
|