Skip to content
Renaud Guillard edited this page Jun 3, 2013 · 8 revisions

UNIX shell command line parser API

This document describes the functions and variables generated by [build-shellscript](https://github.com/noresources/ns-xml/wiki/build-shellscript)

  • [UNIX shell command line parser API](#UNIX shell command line parser API)
    • API
    • Tweaking
      • [Subcommand option variable names](#Subcommand option variable names)
      • Preprocessing
    • [Code snippets](#Code snippets)
      • [Calling the parser](#Calling the parser)
      • [Accessing positional arguments](#Accessing positional arguments)
      • [Dealing with exclusive groups](#Dealing with exclusive groups)

API

Any undocumented function you may find in a generated script should have to be considered as a private function.

Functions

Name Parameters Return value Description
`parse()` `argv` The number of error encountered Parse the command line arguments and fill the options bound variable and the `$parser_values` array
`parse_displayerrors()` None 0 Output all errors generated while parsing the command line
`usage()` `[topic]` 0 Print the program help string, generated from the information gathered in the XML program description file. if *`topic`* is the name of a subcommand, `usage` will display the program's subcommand help string.

Variables

Name Type Description
`$parser_shell` string The name of the shell interpreter (example: `bash`)
`$parser_startindex` int Indicates what is the first valid index in a array variable (0 in [bash](http://www.gnu.org/software/bash/), 1 in [zsh](http://www.zsh.org/))
`$parser_subcommand` string Name of the selected program subcommand. The name is always the one described by the `<name />` element even if one of it aliases was used on the command line
`$parser_values` array Array of positional arguments which are not subcommand names, option names, option arguments nor special markers

Options bound variables

For each program option defined in the program interface XML definition file where a variable name is defined,

#!xml
	&lt;!-- (...) --&gt;
	&lt;databinding&gt;
		&lt;variable&gt;displayHelp&lt;/variable&gt;
	&lt;/databinding&gt;
	&lt;!-- (...) --&gt;

a shell variable of the same name is created with the following rules

Option type Variable type Default value
Switch Boolean `false`
Single argument String Value of `<default/>` if any, Empty string otherwise
Multi argument Array Empty
Group String Empty string

After parsing, variables are modified as is

Option type Value
Switch `true` if the option is present on the command line
Single argument Value of the option argument if the option is present on the command line.
Multi argument Values of the option arguments if the option is present on the command line.
Group If the group is exclusive: Name of the first sub option bound variable name found on the command line; Otherwise, an empty string

Tweaking

Subcommand option variable names

The program interface definition schema allows options of two subcommands to share the same bound variable name. To avoid name aliasing while generating the bash code, the subcommand option variable names can be prefixed with the name of the subcommand using the --prefix-sc-variables option of [build-shellscript](https://github.com/noresources/ns-xml/wiki/build-shellscript) If the subcommand sub has an option with a bound variable name myVar, the variable name will become sub_myVar.

Preprocessing

Any option's bound variable can be modified prior to calling the parse function. This can be useful to set a default value which depends on dynamic data

#!bash
# ... auto generated function and variables are above 

# Set a default value for ${outputFile} using the current date
outputFile="/tmp/filename-$(date +%F).tmp"

# Call parser
if ! parse "${@}"
then
	exit 1
fi

echo "outputFile = ${outputFile}" 
exit 0

Code snippets

Calling the parser

We assume the program has a --help switch option with a bound variable name displayHelp

#!xml
	&lt;!-- (...) --&gt;
	&lt;switch id="prg.option.displayHelp"&gt;
		&lt;databinding&gt;
			&lt;variable&gt;displayHelp&lt;/variable&gt;
		&lt;/databinding&gt;
		&lt;documentation&gt;
			&lt;abstract&gt;Display program usage&lt;/abstract&gt;
		&lt;/documentation&gt;
		&lt;names&gt;
			&lt;long&gt;help&lt;/long&gt;
		&lt;/names&gt;
		&lt;ui mode="disabled" /&gt;
	&lt;/switch&gt;
	&lt;!-- (...) --&gt;

The first lines of your program should looks like

#!bash
# Call the parser
if ! parse "${@}"
then
	# If an error occurs but the --help switch is present, just print help
	if ${displayHelp}
	then
		usage ${parser_subcommand}
		exit 0
	fi
	
	# else, display errors and exit
	parse_displayerrors
	exit 1
fi

# Handle --help option
if ${displayHelp}
then
	usage ${parser_subcommand}
	exit 0
fi

# The real work begins...

Accessing positional arguments

Let's say we are re-coding the mv utility. The n-1 positional arguments are the input files, the last positional argument is the output file or folder

#!xml
	&lt;!-- (...) --&gt;
	&lt;!-- some options --&gt;
	&lt;!-- (...) --&gt;
	&lt;values&gt;
		&lt;other&gt;
			&lt;type&gt;
				&lt;path access="r" /&gt;
			&lt;/type&gt;
		&lt;/other&gt;
	&lt;/values&gt;
	&lt;!-- (...) --&gt;

To process positional arguments, use the ${parser_values} array

#!bash
total_values=${#parser_values[*]}
if [ ${total_values} -lt 2 ] 
then
	echo "Missing arguments"
	exit 2
fi

is_multi_copy=false
if [ ${total_values} -gt 2 ] 
then
	is_multi_copy=true
fi

# This is for Bash
output_index=$(expr ${total_values} - 1)
# And this is for Zsh
# output_index=${total_values}

output=${parser_values[${output_index}]}
if ${is_multi_copy} &amp;&amp; [ ! -d "${output}" ]
then
	echo "${output} have to be a folder"
	exit 1
fi

for ((i=0;${i}&lt;${output_index};i++))
do
	echo "Moving ${parser_values[${i}]} to ${output}"
done

Dealing with exclusive groups

Three single-argument options in an exclusive group.

#!xml
	&lt;group type="exclusive"&gt;
		&lt;databinding&gt;
			&lt;variable&gt;groupVariable&lt;/variable&gt;
		&lt;/databinding&gt;
		&lt;options&gt;
			&lt;argument&gt;
				&lt;databinding&gt;
					&lt;variable&gt;firstArg&lt;/variable&gt;
				&lt;/databinding&gt;
				&lt;names&gt;
					&lt;long&gt;basic-argument&lt;/long&gt;
				&lt;/names&gt;
			&lt;/argument&gt;
			&lt;argument&gt;
				&lt;databinding&gt;
					&lt;variable&gt;secondArg&lt;/variable&gt;
				&lt;/databinding&gt;
				&lt;names&gt;
					&lt;long&gt;string-argument&lt;/long&gt;
				&lt;/names&gt;
				&lt;type&gt;
					&lt;string/&gt;
				&lt;/type&gt;
			&lt;/argument&gt;
			&lt;argument&gt;
				&lt;databinding&gt;
					&lt;variable&gt;thirdArg&lt;/variable&gt;
				&lt;/databinding&gt;
				&lt;names&gt;
					&lt;long&gt;argument-with-default&lt;/long&gt;
				&lt;/names&gt;
				&lt;default&gt;Default value&lt;/default&gt;
			&lt;/argument&gt;
		&lt;/options&gt;
	&lt;group&gt;

Since ${thirdArg} will always have a non-null value (due to the &lt;default/&gt; clause), we can't rely on variable emptyness to know which option the user selects. We have to use the group bound variable ${groupVariable}

#!bash
case "${groupVariable}" in
	"firstArg")
		echo "You select the first option: ${firstArg}"
		;;
	"secondArg")
		echo "You select the second option: ${secondArg}"
		;;
	"thirdArg")
		echo "You select the third option: ${thirdArg}"
		;;
	*)
		echo "Well, since the group is not required, it's ok to select none"
		;;
esac

The program interface definition framework

Clone this wiki locally