CLI Flags in Practice + How to Make Your Own CLI Command with oclif
Chris Castle
Posted on May 9, 2019
Editor's note: If you like CLIs, you should check out oclifconf taking place on Friday, May 31st, 2019 in San Francisco. It’s the first community get-together for oclif! Space is limited so let us know soon if you are interested in joining.
What is it that makes working from the command line so empowering? It can feel archaic at times, sure, but when you remember the right sequence of words, characters, and symbols for what you’re trying to do, it hits you with a sense of accomplishment and mastery over your tools that no graphical interface can compete with.
So what better way to continue your adventures as a developer than by developing your own CLI tool?
In this post, we’ll go over what type of parameters CLI commands take—also known as "flags", "arguments", and sometimes "options.” Then, we’ll start you off with oclif, the CLI framework that makes it easy to create new CLI commands!
The Syntax of a CLI Command
Any command line interface command has a few standard "parts of speech.” As a user of CLI tools, knowing these parts of speech can help you make fewer typos. It can also help you understand complex commands other people share with you more quickly (like these). If you are designing a CLI tool it is even more important to understand these parts of speech, so you can come up with the most ergonomic interface for your users. Yes, a CLI is a user interface!
Some of you may recognize diagrams like the one below from elementary or primary school. Fortunately, understanding how CLI commands are structured isn’t going to feel like this.
CLI commands are pretty straightforward compared to the typical English sentence.
For starters, let’s look at the parameters that appear to the right of CLI commands. Sure, there are many ways you can pass data to a CLI command, but these three types of parameters to the "right" of the command might be the most common: argument, long flag, and short flag. These two formats for flags are the standard for GNU-style flags. Not all CLIs follow this convention, but it has become the most popular style on Unix-like and POSIX compliant operating systems.
What better way for us to start than with the ls command? It’s one of the most common and simplest commands on Unix-like operating systems. It simply lists the contents of a directory.
Command
$ ls
This command, ls, works on its own, as a standalone command. Without any parameters, this command will list the contents of the current directory.
Argument
$ ls .
But you can do the same thing with an argument! Turns out that ls . and ls are the same thing, with ls simply using an implied . directory. For those that don’t remember or don’t know, . always refers to the current directory.
But now, the argument syntax makes it possible for you to pass any directory path to ls, and to get a look at what’s in there.
$ ls /home/casey/code/some-repo-name
An argument is anything to the right of a command that is not a flag (we’ll get to flags next). And fortunately, an argument can come before or after flags–it can coexist with them happily.
Long Flag
To list files that are normally hidden (like ~/.bashrc), you can use a flag on the ls command. ls --all is the long flag form. A long flag always uses a double dash, and it is always represented by multiple characters.
$ ls --all
$ ls . --all
Short Flag
There is also a short flag form of this flag: ls -a. The a is short for all in this case. A short flag always uses a single dash, and it is always represented by a single letter.
$ ls -a
$ ls . -a
Short flags can stack too, so you don't need a separate dash for each one. Order does not matter for these, unless passing a flag argument.
$ ls -la
Flag Argument
Many flags accept an option called a "flag argument" (not to be confused with a "command argument"). In general a command's parameters can be in any order, but flags that accept options must have the option directly after the flag. That way, the command doesn’t get confused by non-flag arguments.
For an example, here the -x flag does not accept an option but the -f flag does. archive.tar is the option being passed to -f. Both of these are valid.
$ tar -x -f archive.tar
$ tar -xf archive.tar
A flag and its option can be separated by a space or an equals sign =. Interestingly, short flags (but not long flags) can even skip the space, although many people find it much easier to read with the space or equals sign.These three are all valid and equivalent.
$ tar -f archive.tar
$ tar -f=archive.tar
$ tar -farchive.tar
Long flags must have a space or equals sign to separate the flag from its option.
We've covered parameters, which are arguments, long flags, and short flags. There are two other ways to pass data to a command: environment variables ("env vars"), or standard input ("stdin"). These won't be covered in this blog post, but check out the links to learn more about them.
Building a New Command With oclif
Scenario: we want to design an oclif command that takes an input like "Casey", and returns "hi, Casey!". There are many ways the user could pass this in. Here we show an example of each type of input using an argument, a long flag, and a short flag.
First, let’s get started with oclif. Getting a CLI app going is very, very straightforward with it. Open up your terminal and type the following, which will use npx to run oclif and then create a new CLI. npx is a pretty useful command to simplify running CLI tools and other executables hosted on the npm registry.
$ npx oclif single greet-me
We won’t go into the details of the single (vs multi) argument above. Check out the oclif documentation for more about this.
Now, you’ll get the chance to specify some details of your new CLI, including the command name. When it asks you, just press enter, choosing the deafult. It’ll take the greet-me argument you passed into the above command. You can choose the default for most of the questions it asks you. The answers won't make much of a difference for this simple tutorial. However, they are very important to answer accurately if you will be sharing your CLI command with others.
? npm package name: greet-me
? command bin name the CLI will export: greet-me
...
Created greet-me in /home/casey/code/greet-me
Now that we have things set up, let’s check out what’s happening in /greet-me/src/index.ts, where all the important argument and flag-handling code for your CLI will live.
const{Command,flags}=require('@oclif/command')classGreetMeCommandextendsCommand{asyncrun(){const{flags}=this.parse(GreetMeCommand)constname=flags.name||'world'this.log(`hello ${name} from ./src/index.js`)}}GreetMeCommand.description=`Describe the command here
...
Extra documentation goes here
`GreetMeCommand.flags={// add --version flag to show CLI versionversion:flags.version({char:'v'}),// add --help flag to show CLI versionhelp:flags.help({char:'h'}),name:flags.string({char:'n',description:'name to print'}),// flag with no value (-f, --force)force:flags.boolean({char:'f'}),}module.exports=GreetMeCommand
What we can see here is that it accepts a few different flag names out the gate (version, name, help, and force) by registering them in the flags object.
Here, with the version flag, the key acts as the ‘version’ long flag name, and on the right side of the expression, we use the method's in oclif’s flags module to register a flag, a type it’ll return, and the short flag name.
Now, we’re ready to rock: let’s see just how many things oclif handles out of the box by running the CLI. Right now, it’s only available with a slightly awkward command.
$ ./bin/run
But npm allows us to symlink this to the name of our CLI.
$ npm link
...
$ greet-me
> hello world from ./src/index.ts
Excellent! Try passing your name in using -n or --name next–and see if there are any other ways oclif will let you pass in arguments.
SIGTERM
While that’s all we’re going to cover in this blog post, oclif has a growing community and its code is open source so there are lots of other ways to learn more. Here are some links to continue exploring oclif.
An episode of the Code[ish] podcast about oclif with Jeff Dickey, one of the creators of oclif, and Nahid Samsami, Heroku’s PM for oclif
The Getting Started tutorial is a step-by-step guide to introduce you to oclif. If you have not developed anything in a command line before, this tutorial is a great place to get started.
Currently, Node 18+ is supported. We support the LTS versions of Node. You can add the node package to your CLI to ensure users are running a specific version of Node.