-
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 you want to collect two things - an application name 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 probably 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 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 have an -m
option defined potentially causing ambiguity issues as -m
might also be passed as a value to --args
. This issue is cancelled out by stopAtFirstUnknown
which causes any arg including and beyond the first unknown to be added to _unknown
. So, in this case our first -m
correct sets the more
option and our second -m
is collected in the _unknown
property.
$ node tmp/--.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 --args
passed we 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 --
as the opening arg in the list of argument 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 }