-
Notifications
You must be signed in to change notification settings - Fork 105
Terminate parsing with a double hypen
A common pattern with command line applications is to use --
to terminate parsing, leaving all remaining arguments to be treated as unknown values even if they begin with a hyphen.
Say your command line application needs to collect two pieces of information - the name of a separate application and a list of arguments to run it with. We expect passing a list of program arguments (e.g. -m My message
etc) to be problematic as some of the values passed may resemble options themselves, causing ambiguity issues.
const commandLineArgs = require('command-line-args')
const optionDefinitions = [
{ name: 'app' },
{ name: 'args', multiple: true }
]
const options = commandLineArgs(optionDefinitions)
console.log(options)
Now, we would like to run this script passing in git
to --app
and merge --squash -m My commit message
to --args
...
$ node example.js --app git --args merge --squash -m My commit message
This script will crash, throwing an UNKNOWN_OPTION: Unknown option: --squash
exception. This is due to ambiguity - some of the values (--squash
and -m
) passed to --args
look like options themselves.
We can address the ambiguity by using --option=value
notation but it's possibly too much to expect the user to append --args=
to each value resembling an option.
$ node example.js --app git --args merge --args=--squash --args=-m My commit message
{ app: 'git',
args: [ 'merge', '--squash', '-m', 'My', 'commit', 'message' ] }
Another method is to use partial parsing, defining only the --app
option and leaving everything else to be returned in _unknown
.
const commandLineArgs = require('command-line-args')
const optionDefinitions = [
{ name: 'app' }
]
const options = commandLineArgs(optionDefinitions, { partial: true })
console.log(options)
Here's a usage example.
$ node example.js --app git merge --squash -m My commit message
{ _unknown: [ 'merge', '--squash', '-m', 'My', 'commit', 'message' ],
app: 'git' }
This gives us the output we need, with our app
name and application arguments grouped together under _unknown
but ambiguity problems will arise if we later add an -m
option to our list of option definitions.
Ambiguity issues can be avoided by using stopAtFirstUnknown
.
const commandLineArgs = require('command-line-args')
const optionDefinitions = [
{ name: 'app' },
{ name: 'more', type: Boolean, alias: 'm' }
]
const options = commandLineArgs(optionDefinitions, { stopAtFirstUnknown: true })
console.log(options)
In this case, we now have an -m
option defined potentially causing ambiguity issues as -m
might also be passed in the arbitrary list of application args we wish to collect. This issue is cancelled out by stopAtFirstUnknown
which causes any arg including and beyond the first unknown to be added to the _unknown
list. So, in this case our first -m
correctly sets the more
flag and our second -m
is collected within the _unknown
list.
$ node example.js --app git -m merge --squash -m My commit message
{ _unknown: [ 'merge', '--squash', '-m', 'My', 'commit', 'message' ],
app: 'git',
more: true }
However, if the user changes the order of args passed making -m
the first in the list of arbitrary application args passed we again have an ambiguity issue.
$ node example.js --app git -m -m My commit message
/Users/lloyd/Documents/75lb/command-line-args/lib/option.js:41
throw err
^
ALREADY_SET: Singular option already set [more=true]
The way to avoid this (and all remaining issues) is to use the conventional --
string as the opening arg in the list of values you'd like the parser to ignore. The final command would look something like this.
$ node example.js --app git -m -- merge --squash -m My commit message
{ _unknown: [ '--', 'merge', '--squash', '-m', 'My', 'commit', 'message' ],
app: 'git',
more: true }