Command-Line Interfaces: Structure & Syntax
Tags: terminal, cli, commandline, documentation
Published: 2021-04-19 12:30:30

---
title: "Command-Line Interfaces: Structure & Syntax"
published: true
description: "My research about command-line interfaces: their structure, syntax, and conventions."
tags: terminal, cli, commandline, documentation
cover_image: https://i.imgur.com/6mpS2oq.png
---

If you are a developer, chances are you have used or read about CLIs already (maybe to execute git or npm commands or in some library's documentation). 

I work with multiple CLIs every day (docker, dotnet, npm, git, etc.), but I had never stopped to learn much about their structure or syntax until recently. Reading documentation and examples is usually enough to get by. 

However, I'm currently developing my own CLI for a personal project, so I decided to learn more about **CLI design, structure, syntax, and conventions**. I did my research, which I documented and decided to share here in this post.

- [1. Introduction](#1-introduction)
  - [1.1. Structure](#11-structure)
- [2. Arguments](#2-arguments)
- [3. Options](#3-options)
  - [3.1. Flags](#31-flags)
- [4. The --help flag](#4-the---help-flag)
  - [4.1. Common aliases for the --help flag:](#41-common-aliases-for-the---help-flag)
  - [4.2. Levels of verbosity](#42-levels-of-verbosity)
  - [4.3. The help command](#43-the-help-command)
- [5. CLI description syntax conventions](#5-cli-description-syntax-conventions)
  - [5.1. Required Parameters](#51-required-parameters)
  - [5.2. Optional Parameters](#52-optional-parameters)
  - [5.3. Arguments that can receive many values](#53-arguments-that-can-receive-many-values)
  - [5.4. Mutually exclusive arguments](#54-mutually-exclusive-arguments)
- [Conclusion](#conclusion)
- [6. References](#6-references)

# 1. Introduction

A CLI or Command Line Interface is an application that **handles user interaction through the Command Line**. 

They don't have graphics (except, maybe, for ASCII Art 😂).
All the **information is presented** to the user **in the form of text** and **the user interacts** with it by **typing in commands**.

CLIs can be faster than GUIs for certain tasks (especially for monotonous tasks and when you're already familiar with the commands). One of the main advantages they have over GUI is that they're **easier to *automate*** (running "npm install" in a CI pipeline could be a lot trickier without a CLI).

## 1.1. Structure
Most CLIs are designed with the following structures:

- `<command> [arguments] [options]`
  - Example: `ls /home/dir --all`
- `<program> <command> [arguments] [options]`
  - Example: `git fetch origin main --depth=10`
- `<program> [arguments] [options]`
  - Example: `docker -v`

![Example: ls /home/my-dir --all](https://i.imgur.com/lG5NuVa.png)

![Example: git fetch origin main --depth=10 ](https://i.imgur.com/KmzktBL.png)

Commands are usually represented as *verbs* and programs as *nouns* (as they are usually a short version of the program's name). There are some cases where a second (or third, or more) noun is used to narrow the scope in which the command will operate, for example:

```sh
docker container start
```
> Here, we have the program (docker), the context (container), and the command (start).

# 2. Arguments

Arguments in a CLI allow **users to send data** to the application, sometimes in a command context. They are often called *positional arguments* because they are **identified by their position**, which means users must write the value for each argument in the *correct position*.

```
mycli command argValue1 argValue2
              |_______| |_______|
                  0         1
```

They can be required or not. If they are not required, the command's behavior may vary in the absence of the parameter, often using a *default value* instead.

```sh
# No argument provided -> Installs all dependencies from the package.json
> npm install

# Argument value: some-package -> Installs only some-package
> npm install some-package
```

Another example:

```sh

# No arguments -> Uses default remote and branch.
> git fetch

# Passing a specific remote and branch as arguments.
> git fetch origin main
```

# 3. Options

Options are **named parameters** that can be passed to a command and are represented by **key-value pairs**.

Unlike positional arguments, their **position is not important**.

You're probably familiar with the following example:
```sh
# Key: --message 
# Value: "commit message"
git commit --message "commit message"
```

Options are usually (not always) used to represent **optional** parameters. In most cases, if a parameter is required, a positional argument is the best way to go.

Some options have **aliases**, which are **short versions** of the same option, easier to type and remember. They're usually identified by a single dash prefix:

```sh
# Long version
> mycli --help

# Alias
> mycli -h
```

Different *delimiters* are supported, depending on the CLI and Operating System. These are some of the most common:

```sh
# Single space
> mycli --file text.txt

# =
> mycli --file=text.txt

# :
> mycli --file:text.txt
```


## 3.1. Flags

Options that **don't require a value** are often called Flags. They are boolean, meaning their presence indicates "true" and their absence "false".

Some examples of commands using flags:
```sh
> git push --force

> npm install --save-dev
```

# 4. The --help flag

Informing our users about the **available commands** and their **arguments and options** can be *tricky without a GUI*. 
That's when the `--help` flag comes in.

When we include the help flag after a command, we ask the CLI to give us more information about it. 
Usually, this information will include a short description of the command, arguments, options, and aliases.

**Example:**

```sh
> npm install --help
```

## 4.1. Common aliases for the --help flag:

```
-help, -h, -?, -H
```

## 4.2. Levels of verbosity

Some CLIs allow the user to ask for **different levels of help**. 
For example, the dotnet CLI will print the short version when we use `-h` or `--help` , and the long version when we use `-H` or `-HELP`.

```sh
# Short version
dotnet <command> -h

# Long version
dotnet <command> -H
```

Similarly, git CLI prints a summary of the command briefly explaining how to use it when we use `-h`, but redirects to the offline HTML documentation when we use `--help`:

```sh
# Short version
git <command> -h

# Offline HTML docs
git <command> --help
```

Learning about the different levels of verbosity was really helpful (I've been using it frequently), although it's a shame there's not a standard for it.

## 4.3. The help command

Some CLIs also provide a **help command**, which usually gives more detailed information than the `--help` flag:  
```sh
program help <command-name>
```

For example, both dotnet CLI and npm CLI will open a browser and redirect you to the command's full documentation when we use the help command: 

```sh
dotnet help <command>

npm help <command>
```

# 5. CLI description syntax conventions

When reading a CLI's documentation or a "man" page, you'll likely come across a particular **syntax used to describe commands**, their arguments, and options (located often under "Synopsis"). 
I used to not pay much attention to it (other than trying and figuring out the general anatomy of the command) and going straight to the examples. But after learning about it and how to interpret it, these descriptions have become much more useful to me.

## 5.1. Required Parameters

Required parameters are often represented using just the name of the param, although in some cases, you may find them between angle brackets:
- `dotnet new <template>`: We can't create a new dotnet project without specifying a template.
- `mv source dest`: When moving a file/directory, we must specify the source and destination parameters.

## 5.2. Optional Parameters

Optional parameters are most commonly represented using square brackets: `mycli command [optionalParameter]`
- `docker container ls [options]` We can add some flags and options to alter the command's behavior.
- `git remote [-v | --verbose]` We can use -v or --verbose to get more information about each git remote.

## 5.3. Arguments that can receive many values

**Ellipses** indicate the argument/option expects **many values**. It can be applied to optional or required parameters 

Here's how it looks like applied to **optional parameters**:
```sh
mycli command [parameter...]
```

The previous statement tells us our parameter expects **0 to N values**.

**Required parameters** that receive many values are usually represented in one of two ways. Here is one of them:

```sh
mycli command <myParameter> [myParameter...]
```

This statement tells us the command receives at least a single value for  `<myParameter>` but can also receive more values, represented by `[myParameter...]` **resulting in 1 to N values**.

Here's an example from the Docker CLI:

```sh
docker container start <container> [container...]
```
>When executing the start command, we must specify at least one container, but we can also start more than one in the same command.

The second way of representing required parameters with many 1 to N values is as follows:
- `mycli command <myParameter...>`
- Or: `mycli command myParameter...`

Since we are not using square brackets, it's clear `myParameter` is required, which indicates it needs **at least one value (1 to N)**.

Here's an example:
```sh
mkdir directory...
```
> We need to specify at least one directory to create when using mkdir, but we can also create more than one.

## 5.4. Mutually exclusive arguments

Some arguments **cannot be used together** in the same command. Their *exclusive relationship* is represented using pipes:
```sh
mycli command [ --option1 | --option2 ]
```
The square brackets tell us we *don't have to* use any of those options, and the pipe tells us that, if we decide to use them, we can't use them both at the same time. So any of the following are valid:

- `mycli command`
- `mycli command --option1`
- `mycli command --option2`

But the following would be invalid:
- `mycli command --option1 --option2`

An example from git commit:
```sh
git commit [ -a | --interactive | --patch ]
```
- The previous statement tells us we can use any of those flags (`-a`, `--interactive` or `--patch` ) but not together
- Also, the square brackets indicate we can also decide not to use any of them.

Here's what happens when we try to use `-a` and `--patch` together:
```sh
> git commit -a --patch 

fatal: Only one of --include/--only/--all/--interactive/--patch can be used.
```

This syntax is often used to represent **aliases**: `git remote [ - v | --verbose ]`. Both flags are valid individually, but it doesn't make sense to use them simultaneously.

Similarly, there are cases when **at least one option must be included**. In those cases, we use vertical bars, and we group the parameters using curly brackets or parentheses, like so:
- `mycli command { --option1 | --option2 }`
- Or:  `mycli command ( --option1 | --option2 )`

The curly brackets or parentheses indicate that at least one option must be included.

Here's an example from `git remote`:
```sh
git remote set-head <name> (-a | --auto | -d | --delete | <branch>)
```
- The **parenthesis** indicates that we must **choose at least one** of those options
- The **pipes** tell us **we can't choose two or more** of them

If we don't use any of the required options, we get this error:

```sh
> git remote set-head origin 

usage: git remote set-head <name> (-a | --auto | -d | --delete | <branch>) 
    -a, --auto            set refs/remotes/<name>/HEAD according to remote 
    -d, --delete          delete refs/remotes/<name>/HEAD
```

Another example from `dotnet new`:
```sh
dotnet new <template> [-lang|--language {"C#"|"F#"|VB}]
```
- So, the **square brackets** indicate that passing a `-lang`  or `--language`  is optional
- The **curly braces** and **pipe** indicate that we must choose between C#, F#, and VB.

# Conclusion
I know I could have developed my CLI without stopping to learn any of this. After all, there are libraries that would take care of the syntax and interpretation of commands. Nonetheless, I found it extremely interesting, and I have been using this new knowledge a lot after acquiring it. Finding and reading CLIs documentation has become a lot easier, and I learned many tips that will definitely help me develop a better, more intuitive CLI following the conventions and standards described in this article.

# 6. References
- [Command Line Interface - Codeacademy](https://www.codecademy.com/articles/command-line-interface)
- [Documenting command-line syntax - Google Developer Documentation](https://developers.google.com/style/code-syntax)
- [.NET CLI documentation](https://docs.microsoft.com/en-us/dotnet/core/tools/)
- [Git CLI documentation](https://git-scm.com/docs)
- [NPM CLI documentation](https://docs.npmjs.com/cli/v7/commands)