A script to parse command line arguments AND generate the help text based on a JSON argument spec.
I have a very pleasant experience parsing command line arguments in C# with the help of the wonderful Mono.Options package. It allows to declare the spec for the command line arguments declaratively and automatically generates a nice looking help text. Which is extremely convenient. Much recommended.
There is a lot to say about Powershell, both good and bad, but one thing is sure for me - they nailed it with the way they handle command line arguments to scripts and functions. An effortless experience, very convenient.
Many examples I saw include 3 elements (more or less):
- a call to
getopt
orgetopts
(or some other equivalent) - a big loop with a case statement inside
- a separate help function outputting the help text
Of course, it is on us to make sure the help text accurately reflects what we parse in the loop.
This is not the way I am used to parse command line arguments. I want a solution satisfying the following conditions:
- The supported command line options are declared using a specification.
- The help text is to be generated automatically from the specification.
- An option can be declared as required.
- An option can be declared as having a mandatory value.
This StackOverflow question seems to have a few answers that do that. I consolidated them in my own answer, where I am linking to the answers which give the respective solutions.
They are likely better than mine, but I wanted to try it anyway :-).
Internally, my script uses the getopt
bash command AND it depends on the jq
tool, because the command line argument spec is in JSON.
- Options with mandatory arguments
- Required options
- Options with mandatory arguments from the supported values
- Options with the default value
- Auto generated help text
- Long/short options as supported by the
getopt
command
Does not support command line parameters with optional arguments. Either a parameter does not accept any value (i.e. it is a boolean switch) or a value must always be given.
The repository contains an example script (example.sh) showing off the capabilities of my command line parser:
/c/work/bash-parse-command-line-args (master)$ ./example.sh
example.sh: APP_NAME value must be given (-n | --name | --app-name)
/c/work/bash-parse-command-line-args (master)$
/c/work/bash-parse-command-line-args (master)$ ./example.sh -h
Command line options:
-h, --help Show this help text
-n, --name, --app-name=value [REQUIRED] A comma separated list of application names or the keyword 'all' to run the given build for all the relevant applications
--number=value [REQUIRED] A number representing one of the AKS root modules
The supported values: '100','200','300','400'
-k, --kind=value The validation kind
The supported values: 'strict','relaxed'
The default value: 'strict'
-b, --build=value [REQUIRED] A build definition name
--nn, --no-navigate Do not navigate to the build pages
-p, --plan Plan only, do not apply
-s, --skip, --skip-internal-dns Skip the internal DNS stages
/c/work/bash-parse-command-line-args (master)$
/c/work/bash-parse-command-line-args (master)$ ./example.sh -psn xyz -b mybuild --nn --num 100 -k relaxed
APP_NAME=xyz
NUMBER=100
BUILD_DEF_NAME=mybuild
KIND=relaxed
NO_NAVIGATE=1
TERRAFORM_PLAN=1
SKIP_INTERNAL_DNS=1
/c/work/bash-parse-command-line-args (master)$
/c/work/bash-parse-command-line-args (master)$ ./example.sh --plan --app-name xyz --build mybuild --no-navigate --skip-internal-dns --number 100 --kind relaxed
APP_NAME=xyz
NUMBER=100
BUILD_DEF_NAME=mybuild
KIND=relaxed
NO_NAVIGATE=1
TERRAFORM_PLAN=1
SKIP_INTERNAL_DNS=1
/c/work/bash-parse-command-line-args (master)$
/c/work/bash-parse-command-line-args (master)$ ./example.sh --app xyz -b mybuild --num 200
APP_NAME=xyz
NUMBER=200
BUILD_DEF_NAME=mybuild
KIND=strict
NO_NAVIGATE=
TERRAFORM_PLAN=
SKIP_INTERNAL_DNS=
/c/work/bash-parse-command-line-args (master)$
/c/work/bash-parse-command-line-args (master)$ ./example.sh --app xyz -b mybuild -x
example.sh: unknown option -- x
/c/work/bash-parse-command-line-args (master)$
/c/work/bash-parse-command-line-args (master)$ ./example.sh --app xyz -b
example.sh: option requires an argument -- b
/c/work/bash-parse-command-line-args (master)$
/c/work/bash-parse-command-line-args (master)$ ./example.sh --app xyz -b mybuild --num 500
example.sh: The given (--number) value of '500' is not equal any of the supported values '100','200','300','400'
/c/work/bash-parse-command-line-args (master)$
/c/work/bash-parse-command-line-args (master)$ ./example.sh --app xyz -b mybuild -k xyz --num 100
example.sh: The given (-k | --kind) value of 'xyz' is not equal any of the supported values 'strict','relaxed'
/c/work/bash-parse-command-line-args (master)$
I am very much interested in improving the quality and the performance of my script, so any relevant Pull Requests are more than welcome.