Commander.js: how to use sub command with option?

Created on 7 Apr 2016  路  13Comments  路  Source: tj/commander.js

Hi I am trying sub command with option. Is there an example? I tried below code. How can I get the option value from the action callback?

  .command('upload')
  .description('upload a file')
  .option('-f, --file <file>', 'file name')
  .action(function(file) {
    console.log("uploading...");
    console.log(file);
  });

 program.parse(process.argv);

$ node index.js upload -f filename
uploading...
Command {
commands: [],
options:
[ Option {
flags: '-f, --file ',
required: -12,
optional: 0,
bool: true,
short: '-f',
long: '--file',

Most helpful comment

If you want git-style sub commands this is something I would do:

/var/foo/bar/main:

#!/usr/bin/env node
'use strict';
const program = require('commander');

program
    .version('0.0.1')
    .command('create', 'command description')
    .parse(process.argv);

/var/foo/bar/main-create:

#!/usr/bin/env node
'use strict';
const program = require('commander');

program
    .version('0.0.1')
    .command('cluster','command description')
    .parse(process.argv);

/var/foo/bar/main-create-cluster

#!/usr/bin/env node
'use strict';
const program = require('commander');
program
    .version('0.0.1')
    .option('-n, --name <name>', 'Sets the cluster name')
    .option('-i, --ip <ip>', 'Sets the cluster ip')
    .parse(process.argv);


but note that commander won't show you help usage for options.

All 13 comments

Hi @kylelix7, the last argument passed to the callback is actually an options object so you should be able to get the file name value with options.file.

  .command('upload')
  .description('upload a file')
  .option('-f, --file <file>', 'file name')
  .action(function(options) {
    console.log("uploading...");
    console.log(options.file);
  });

program.parse(process.argv);

If you have arguments available to the command (as in program.command('upload' [fileName])), they will be expected arguments in the callback function. See https://www.npmjs.com/package/commander#examples. Hope this helps!

What if you're using git-style commands in different files? The options don't seem to come through when chaining the .action cb

If you want git-style sub commands this is something I would do:

/var/foo/bar/main:

#!/usr/bin/env node
'use strict';
const program = require('commander');

program
    .version('0.0.1')
    .command('create', 'command description')
    .parse(process.argv);

/var/foo/bar/main-create:

#!/usr/bin/env node
'use strict';
const program = require('commander');

program
    .version('0.0.1')
    .command('cluster','command description')
    .parse(process.argv);

/var/foo/bar/main-create-cluster

#!/usr/bin/env node
'use strict';
const program = require('commander');
program
    .version('0.0.1')
    .option('-n, --name <name>', 'Sets the cluster name')
    .option('-i, --ip <ip>', 'Sets the cluster ip')
    .parse(process.argv);


but note that commander won't show you help usage for options.

Have a related question about subcommands... what if want to use common options across subcommands. Based on the above it seems I have to define them in all my subcommands. Am I missing something?

For example, I want to have a -l --loglevel [debug|info|warn|error] option for all my commands & subcommands. It seems I need to set a program.option in each top level command and all subcommands.

I guess I could have a function that takes program as a param, or is there a better way?

You can see my current attempts in https://github/dgem/kat-nip

@gentunian Hey what if at the first level, I want to list the available sub-commands of 'create'? For example right now if I do
$cli-name create
by itself it will raise error since I don't give any sub-command.

@frankcchen I cannot reproduce that:

~/code/nodejs/borrar/main.js:

#!/usr/bin/env node
'use strict';
const program = require('commander');

program
    .version('0.0.1')
    .command('create', 'command description')
    .parse(process.argv);

~/code/nodejs/borrar/main-create.js:

```javascript
#!/usr/bin/env node
'use strict';
const program = require('commander');

program
    .version('0.0.1')
    .command('cluster','command description')
    .parse(process.argv);

Test:

[seba@leyla:~/code/nodejs/borrar] 2s $ node main.js 
Usage: main [options] [command]

Options:
  -V, --version  output the version number
  -h, --help     output usage information

Commands:
  create         command description
  help [cmd]     display help for [cmd]
[seba@leyla:~/code/nodejs/borrar] $ node main.js create
Usage: main-create [options] [command]

Options:
  -V, --version  output the version number
  -h, --help     output usage information

Commands:
  cluster        command description
  help [cmd]     display help for [cmd]
[seba@leyla:~/code/nodejs/borrar] $ node main-create.js 
Usage: main-create [options] [command]

Options:
  -V, --version  output the version number
  -h, --help     output usage information

Commands:
  cluster        command description
  help [cmd]     display help for [cmd]

@gentunian You're right.
What if I want to handle unsupported command at the top level aka main.js? Assume your program only has command 'create'. now if I do
$ node main.js bogus
It will have no output.

I have tried adding
.on('command:* , () => {...});
But it didn't really work. The added line intercepts all commands basically.

@frankcchen I think you could try and play with the result of parsing the options/commands.

Play with this:

/var/foo/main.js:

#!/usr/bin/env node
'use strict';
const program = require('commander');

const r = program
    .version('0.0.1')
    .command('create', 'command description')
    .parse(process.argv);

if (r) {
   console.log('I think no matches were found');
}

I've edited the code. I think you could do a workaround just like that. Each sub-command may also follow the same workaround.

For reference what parse() function returns check the source code

I understand the process of how Commander handles subcommands and how each level is a js file in its own right - although i think this is a very cludgy approach.
I will have around 15 first level commands and maybe as many 10 second level commands for each of the first level command followed by optional parameters - so that is going to lead to a huge number of sub files all in the same top level folder - just to manage the process.
for example:
job list -prefix
job status -jname
job purge -jname
job submit -batchfile

in this example job is a top level command category and the second command (list etc) is the action to take within that category. I would like to isolate all the subcommand files relating to jobs into a jobs folder. That way I would at least be able to manage the 15 or so sub folders for my top level commands.

Rather than be fixed at using level 1 file followed by level 2 file having to be in the same folder is
there any means of being able to specify the structure as to where these files can be found?
I am at the point of wondering if Commander is actually the best tool for me to use for the type of Command Line Interface that I want to put in place?

What I would like to do seems reasonable but I have found no tool that will help accomplish what I want to do whilst also providing built in help.

Following seems to work for me - though I'd also appreciate a proper solution. Grouping of commands and options would be nice too. And some option names conflict with the implementation of commander, e.g. 'name' or 'domain'.

    .version('0.1.0')
    .description('CLI');

commander
    .command('deployment <command>')
    .description('Manage deployment')
    .option('')
    .option('create', 'Create deployment')
    .option('--cloud <provider>', 'Cloud provider', 'aws')
    .option('--region <region>', 'Region', 'eu-central-1')
    .option('--prefix <prefix>', 'Resource prefix', 'tld-domain')
    .option('')
    .option('update', 'Update deployment')
    .option('--prefix <prefix>', 'Resource prefix', 'tld-domain')
    .action((command, options) => {
        console.log(command, options);
    });

Can be invoked either way (though the help will produce Usage: deployment [options] <command>):

cli deployment create --prefix=my-domain
cli deployment --prefix=my-domain create

Generated Help - 1st Level

# cli --help
Usage: cli [options] [command]

CLI

Options:
  -V, --version                   output the version number
  -h, --help                      output usage information

Commands:
  deployment [options] <command>  Manage deployment

Generated Help - 2nd Level

# cli deployment --help
Usage: deployment [options] <command>

Manage deployment

Options:

  create              Create deployment
  --cloud <provider>  Cloud provider (default: "aws")
  --region <region>   Region (default: "eu-central-1")
  --prefix <prefix>   Resource prefix (default: "tld-domain")

  update              Update deployment
  --prefix <prefix>   Resource prefix (default: "tld-domain")

There was an answer posted to the original post, which was options for a subcommand with an action handler.

There was an example posted for option getting passed down into sub-sub-command.

563 is an open issue about program level options being available to executable subcommands.

Closing this issue as not covering something that will be actioned. Feel free to open a new issue if it comes up again, with new information and renewed interest.

Thank you for your contributions.

Is there any way to show full help for subcommands commands like npm -l?

@NaveenDA No, see #1078 and #905 for somewhat related issues. Nice example, thanks.

Was this page helpful?
0 / 5 - 0 ratings