|||

Video Transcript

X

CLI Flags in Practice + How to Make Your Own CLI Command with oclif

Editor's note: If you like CLIs, you should check out oclifconf taking place on Friday, May 31st 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.

sentence-diagram

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.

$ git log --pretty=oneline
$ git log --pretty oneline

Other Ways of Passing Data

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')

class GreetMeCommand extends Command {
  async run() {
    const {flags} = this.parse(GreetMeCommand)
    const name = 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 version
  version: flags.version({char: 'v'}),
  // add --help flag to show CLI version
  help: 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.

…
    version: flags.version({char: 'v'}),
…

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.

Browse the archives for engineering or all blogs Subscribe to the RSS feed for engineering or all blogs.