Parseargs is a command line option parser for shell scripts.
This tutorial explains the features of Parseargs using examples.
The first section POSIX Shells treats all functionality that works in a POSIX compliant shell. Also zsh is not POSIX compliant (and never wanted to be), the mentioned features should run with it.
The second section Extended Shells handles those few features that need functionality beyond the POSIX standard.
Currently there is only on feature, that requires the support of array variables.
The shells bash, ksh and zsh provide this.
These shells can be enabled by using the option -s
/ --shell
with the shell name.
When ksh is mentioned in this document, we are talking about ksh92 or newer. Parseargs was never tested with ksh88. |
1. POSIX Shells
1.1. Simple Usage
The following script should support two options and arguments. The options are:
-
-l
to enable "long" output. -
-o <filename>
to define a file to write to
example.sh
1
2
3
4
5
6
7
8
9
#!/bin/sh
eval "$(parseargs -n example.sh -o 'l#long_output,o=outfile' -- "$@")" || exit 1
if [ -n "$long_output" ]; then
echo "Long output is enabled"
fi
echo "Output file: '$outfile'"
echo "Arguments: $*"
Before testing lets look at the options and arguments given to Parseargs.
-n example.sh
-
This tells Parseargs the name of the calling script. The name is used as prefix for error messages.
-o 'l#long_output,o=outfile'
-
This defines two options, separated by a comma.
l#long_output
-
This definition defines a simple flag. The
#
in the middle is the marker for a flag. The part before defines the option character. Herel
defines the option-l
. The part after the#
is the variable that should be set, when-l
was found on the command line. The variable gets the value "true" assigned. o=outfile
-
This definition defines a assignment option. A assignment option needs an additional argument. The
=
in the middle is the marker for a assignment. Again the part before defines the option. Here-o
. The part after the marker is the name of the variable. This variable gets the option argument assigned.
--
-
The double dash separates the Parseargs options from the shell script options.
"$@"
-
This is replaced by the script options. Always use exactly this notation. A simple
$@
(without the quotes) or$*
might fail. See your shell documentation.
The option character may be any ASCII character, except for The characters |
The trailing || exit 1
is just a safety net, in case something goes wrong during eval.
Let’s test the script:
$ ./example.sh -l -o out.file in.file other.file Long output is enabled Output file: 'out.file' Arguments: in.file other.file
No space needed between -o
and the file name:
$ ./example.sh -l -oout.file in.file other.file Long output is enabled Output file: 'out.file' Arguments: in.file other.file
And -l
and -o
can be combined into one:
$ ./example.sh -loout.file in.file other.file Long output is enabled Output file: 'out.file' Arguments: in.file other.file
Options and argument order is not relevant:
$ ./example.sh -l in.file other.file -oout.file Long output is enabled Output file: 'out.file' Arguments: in.file other.file
Using a unknown option:
$ ./example.sh -X example.sh: Unknown option: '-X'
Duplicate options are not allowed:
$ ./example.sh -o out.file -o other-out.file in.file example.sh: Duplicate option: '-o' $ ./example.sh -l -l -o out.file in.file example.sh: Duplicate option: '-l'
1.2. Long Options
A lot of programs support additional long forms of options.
Like -l
and --long
.
Parseargs also supports this:
long-opt.sh
1
2
3
4
5
6
7
8
9
10
#!/bin/sh
eval "$(parseargs -n long-opt.sh -o 'l:long#long_output,o:out-file=outfile' -- "$@")" || exit 1
if [ -n "$long_output" ]; then
echo "Long output is enabled"
fi
echo "Output file: '$outfile'"
echo "Arguments: $*"
Now we have two colon-separated options before the type marker (#
, =
).
If a option is a single character, it defines a short option (l
→ -l
).
With multiple characters it is a long option, that has two leading dashes (long
→ --long
).
Long options may contain any ASCII character, except for The characters |
Now our example script enables long output by either using -l
or --long
and the output file can be set with -o out.file
or --out-file out.file
or even --out-file=out.file
.
Again some tests:
$ ./long-opt.sh --long --out-file out.file in.file other.file Long output is enabled Output file: 'out.file' Arguments: in.file other.file $ ./long-opt.sh --long --out-file=out.file in.file other.file Long output is enabled Output file: 'out.file' Arguments: in.file other.file
Duplicate option detection still works:
$ ./long-opt.sh --long -l long-opt.sh: Duplicate option: '-l/--long'
Long Options and Optional Arguments
With long options an optional argument is supported for flag options.
This optional argument is directly appended to the option with a =
.
The values true
and yes
are interpreted as boolean true and false
and no
as false.
The check is case insensitive.
So, to reuse the example above:
$ ./long-opt.sh --long=true --out-file=out.file in.file Long output is enabled Output file: 'out.file' Arguments: in.file $ ./long-opt.sh --long=yes --out-file=out.file in.file Long output is enabled Output file: 'out.file' Arguments: in.file $ ./long-opt.sh --long=false --out-file=out.file in.file Output file: 'out.file' Arguments: in.file $ ./long-opt.sh --long=no --out-file=out.file in.file Output file: 'out.file' Arguments: in.file $ ./long-opt.sh --long=anything --out-file=out.file in.file long-opt.sh: Invalid boolean value: 'anything'
1.3. Counting Options
Tools sometimes have an option to increase verbosity of the output. Example from the ssh man page:
-v Verbose mode. Causes ssh to print debugging messages about its progress. This is helpful in debugging connection, authentica‐ tion, and configuration problems. Multiple -v options increase the verbosity. The maximum is 3.
Parseargs has an own option type to support this.
A "Counting Option" is defined using the marker +
.
The following script only supports the options -v
and --verbose
.
verbosity.sh
1
2
3
4
5
#!/bin/sh
eval "$(parseargs -n verbosity.sh -o 'v:verbose+verbosity' -- "$@")" || exit 1
echo "Verbosity: $verbosity"
$ ./verbosity.sh Verbosity: 0 $ ./verbosity.sh -v Verbosity: 1 $ ./verbosity.sh -vvv Verbosity: 3 $ ./verbosity.sh -vvvvvvv Verbosity: 7
The long option form additionally supports a optional argument:
$ ./verbosity.sh --verbose Verbosity: 1 $ ./verbosity.sh --verbose -v Verbosity: 2 $ ./verbosity.sh --verbose=5 Verbosity: 5 $ ./verbosity.sh --verbose=full verbosity.sh: Not a valid count value: "full"
The long form with optional argument sets the verbosity, it does not increase it by the given number. |
$ ./verbosity.sh -vv --verbose=5 Verbosity: 5
1.4. Mode Switch Options
A Mode Switch Options are not a new option type, but a extension of a simple flag. Mode switches use one variable with different options and assign different values to the variable.
A simple example would be whether something should be copied or moved.
In that case the option -c
would request to copy and -m
would request move.
The definition of such options look like normal flags, but have a equal sign and a value appended.
mode-switch.sh
1
2
3
4
5
#!/bin/sh
eval "$(parseargs -n mode-switch.sh -o 'c:copy#mode=copy,m:move#mode=move' -- "$@")" || exit 1
echo "Mode: $mode"
And here some tests:
$ ./mode-switch.sh -c Mode: copy $ ./mode-switch.sh -m Mode: move $ ./mode-switch.sh -cm mode-switch.sh: Options are mutual exclusive: -c/--copy, -m/--move
1.5. Required Options
Sometimes a option might be required. Parseargs supports this with a asterisk before the variable name.
required.sh
1
2
3
4
5
#!/bin/sh
eval "$(parseargs -n required.sh -o 'o=*out_file' -- "$@")" || exit 1
echo "Output file: $out_file"
And now a test:
$ ./required.sh -o output.file Output file: output.file $ ./required.sh required.sh: Required option not found: -o
1.6. Showing Help and Version
First up: Parseargs itself does not support creating help texts. But Parseargs can call a existing shell function that prints some help text.
With the Parseargs option -h
/ --help-opt
, the script option --help
is supported.
If the option is given, the shell function show_help
is called and the script is terminated with exit code 0.
To display the script version, the Parseargs option -v
/ --version-opt
enables support for the script option --version
.
If the option is given, the shell function show_version
is called and the script is terminated with exit code 0.
The script must define the named show_*
functions.
If they are not defined, a error message is displayed and the script is terminated.
help.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/sh
show_help()
{
echo "Usage: example.sh OPTIONS <input-file...>"
echo " -l, --long enable detailed output"
echo " -o, --out-file FILE file to write result"
}
show_version()
{
echo "help.sh 1.0"
}
eval "$(parseargs -n help.sh.sh -hv -o 'l:long#detailed,o:out-file=outfile' -- "$@")" || exit 1
Displaying help and version:
$ ./help.sh --help Usage: example.sh OPTIONS <input-file...> -l, --long enable detailed output -o, --out-file FILE file to write result $ ./help.sh --version help.sh 1.0
To better understand how this works, see the sections Using Callbacks and Singleton Options.
1.7. Using Callbacks
Till now we used Parseargs to assign variables for the options found on the command line, but it is also able to work with shell functions.
When using functions, Parseargs also generates code to test for the existence of the function.
Assuming a function set_out_file
should be used, it is always checked whether this function exists.
1
2
3
4
5
# default
if ! LC_ALL=C command -V set_out_file 2>/dev/null | head -n1 | grep function >/dev/null; then echo >&2 "ERROR: Function 'set_out_file' does not exist."; exit 127; fi;
# with --shell bash, ksh or zsh
if ! typeset -f set_out_file >/dev/null 2>&1; then echo >&2 "ERROR: Function 'set_out_file' does not exist."; exit 127; fi;
This code will exit the calling script if the function does not exist. The check is always done, whether the function is needed in the actually generated code or not.
When calling the callback the exit status of the function must be zero else the calling script is terminated with function exit code. The code for this looks like this:
1
set_out_file 'output.file' || exit $?
1.7.1. Callbacks for Options
Instead of assigning variables for options, it is also possible to call a function.
By adding ()
to the name, it defines the function to call.
option-cb.sh
1
2
3
4
5
6
7
8
9
#!/bin/sh
set_long() { echo "set_long($1)"; }
set_outfile() { echo "set_outfile($1)"; }
set_verbosity() { echo "set_verbosity($1)"; }
eval "$(parseargs -n option-cb.sh -o 'l:long#set_long(),o=set_outfile(),v+set_verbosity()' -- "$@")" || exit 1
echo "Arguments: $*"
Testing:
$ ./option-cb.sh -v -l -o out.file -vv input set_verbosity(1) set_long(true) set_outfile(out.file) set_verbosity(3) Arguments: input $ ./option-cb.sh --long=false input set_long() Arguments: input
-
For counting options, the callback might be called multiple times with the current count value.
-
For flags it is called with a value
'true'
. If the option explicitly is set tofalse
using--option=false
, the callback is called with an empty string. -
For assignment options the callback is called with the option argument.
Using a callback disables checks within Parseargs. The duplicate usage of options is not checked and also the duplicate usage of mode-switch options are not detected. With callbacks you have more control and possibilities, but also more responsibilities. |
1.7.2. Callback for Arguments
In the previous sections we have seen callbacks for options, this is also possible for program arguments.
The callback for program arguments is defined with the Parseargs option -a
or --arg-callback
.
args-cb.sh
1
2
3
4
5
#!/bin/sh
set_argument() { echo "set_argument($1)"; }
eval "$(parseargs -n args-cb.sh -a set_argument -o '' -- "$@")" || exit 1
When the argument callback is used, the positional parameters are always empty.
So $1
etc are unset.
1.7.3. Callback on Error
Parseargs allows the defition of an error callback.
This defines a function that is called before Parseargs emits exit 1
to terminate the calling script.
The following example doesn’t support any options and insults you when you give one.
error-cb.sh
1
2
3
4
5
6
7
#!/bin/sh
error_callback() { echo "You did something stupid!"; }
eval "$(parseargs -n error-cb.sh -e error_callback -o '' -- "$@")" || exit 1
echo "OK"
$ ./error-cb.sh OK $ ./error-cb.sh -x error-cb.sh: Unknown option: -x You did something stupid! $
1.8. A Script without Options
Parseargs if even useful in scripts that don’t support any options. In that use case it would output an error message when a option is given.
no-opt.sh
1
2
3
4
5
#!/bin/sh
eval "$(parseargs -n no-opt.sh -p -- "$@")"
echo "Arguments: $*"
In this script we also use the option -p
/ --posix
, then Parseargs stops looking for options as soon as the first program argument is found.
$ ./no-opt.sh first second Arguments: first second $ ./no-opt.sh -X first second no-opt.sh: Unknown option: -X $ ./no-opt.sh first second -X Arguments: first second -X
1.9. The Option Argument Separator '--'
POSIX defines the --
as a separator between options and program arguments.
Reusing our first script example.sh
:
$ ./example.sh -o out.file -X example.sh: Unknown option: -X $ ./example.sh -o out.file -- -X Output file: 'out.file' Arguments: -X
A second --
is handled as a normal argument:
$ ./example.sh -o out.file -- -X -- test Output file: 'out.file' Arguments: -X -- test
1.10. Initializing Variables
With the option -i
/ --init-vars
the variables can be initialized with their default values.
Note, that variables of counting options are always initialized to 0.
This is useful, when the script runs with set -u
to treat unset variables as error.
Note that this is for variables only. Callbacks are not called.
$ parseargs -n example.sh -o 'l#long,o=outfile,v+verbosity' --init-vars -- -o out.file -l long=''; outfile=''; verbosity=0; outfile='out.file'; long='true'; set --
1.11. Singleton Options
A Singleton Option is an option that supersede all other options or arguments on the command line.
The typical use for this is a help option.
In fact the support for --help
is implemented using this.
A option is marked as a by putting a ?
in front of the function name (variables are rarely used here).
The following is used to support --help
help#?show_help()
The ?
tells Parseargs that this is a singleton option and that
-
only this option should be processed.
-
all other content of the command line should be dropped. (The content before the
--help
must still be valid.) -
the calling script should be terminated with exit code 0 if the target is a callback.
So this can be easily used to implement additional help options.
Maybe --help-storage#?show_help_storage()
.
This feature can also be used for other things. The following is from a script that is used as an unpacker for arbitrary archives.
1
eval "$(parseargs -hin ERROR -o "l#mode=listMode,L#mode=topLevelList,D#mode=basenameDir,S#mode=singleDir,T#?checkPrograms(),d=tgtDir,v+verbose" -- "$@")" || exit 1
The interesting part is T#?checkPrograms()
.
This defines the option -T
as a singleton option, that calls the function checkPrograms
.
In the actual script the -T
triggers a test whether the needed tools are available and hence which archive types are supported.
After this test is completed, the script is terminated.
No other actions is performed, independent of other options given on the command line.
2. Extended Shells
This section describes functionalities, that need additional capabilities beyond those defined by POSIX.
2.1. Separating Arguments behind a --
Only supported with shells bash , ksh or zsh .
|
Sometimes it is useful to handle the arguments before and after a --
differently.
The following command monitors the file tutorial.adoc
and executes the command make html
as soon as a change is detected.
$ when-changed tutorial.adoc -- make html
Here the arguments before the --
are different than the arguments behind it.
Parseargs is able to collect the arguments behind the --
in a shell array and leave the arguments before it as positional parameter ($1
…).
The option -r
/ --remainder
is used to define the name of the array.
The Parseargs call for this would look like this:
1
eval "$(parseargs -s bash --remainder cmd -h -- "$@")" || exit 1
Assuming the call above, the name tutorial.adoc
would be available as $1
and the words make
and html
as ${cmd[0]}
and $cmd[1]}
.
Note that in zsh it would be ${cmd[1]}
and $cmd[2]}
, as arrays in zsh are 1-based.
3. Details
3.1. Support for Different Shells
Parseargs supports generating code for different shells. The following shells are supported:
--shell=sh
(the default)-
With this setting, code for a POSIX compliant shell is generated. This should work with any POSIX compliant shell and with zsh. The option
-r
/--remainder
is not supported. --shell=bash
,--shell=ksh
and--shell=zsh
-
This shells support all features of Parseargs. The code generated is (as of today) identical, except for array initialization, which is different in ksh.
3.2. Parseargs and ShellCheck
ShellCheck is a static code analysis tool for shell scripts. If you don’t use it yet, you really should.
As Parseargs is creating and assigning new variables at runtime, ShellCheck can’t know about them and will complain. For our first example the following output would be created:
$ shellcheck ./example.sh In example.sh line 8: echo "Output file: '$outfile'" ^------^ SC2154 (warning): outfile is referenced but not assigned. For more information: https://www.shellcheck.net/wiki/SC2154 -- outfile is referenced but not ass...
The best solution is to initialize default values before calling Parseargs.
Like:
1
2
3
4
5
#!/bin/sh
long_output=
outfile=
eval "$(parseargs -n example.sh -o 'l#long_output,o=outfile' -- "$@")" || exit 1
3.3. Parseargs and Invalid UTF-8
As of today, Parseargs can only handle arguments that are valid UTF-8.
If a invalid UTF-8 character is found, Parseargs will display an error message and exit the calling script.