Does your CLI tool have a man page?

If you're into writing CLI tools in Go, chances are you implement a help sub-command or a --help flag for each one, or have the CLI framework of your choice generate help output for you.

Awesome, but what about man pages? Unless your CLI apps are made for Windows only, you'd surely want to make your CLI tools well-behaved inhabitants of the Unix ecosystem. And this means your CLI app should come with a man page.

Luckily, there are several ways to create man pages for your Go app, including:

  1. Write them manually
  2. Use Pandoc
  3. Use muesli/mango
  4. Use cpuguy83/go-md2man
  5. Use Cobra
  6. Use lufia/godoc2man

So you need at least five excuses for not adding a man page to your CLI project. Better write one!

1. Write them manually

Maybe this comes as a surprise, but manpages are actually plain-text files that use a fairly simple markup language.

A typical manpage is structured like this:

SOMETHING(SECTION)  User Manual  SOMETHING(SECTION)

NAME
  SOMETHING - briefly describe SOMETHING
SYNOPSIS
  formal description of invocation
DESCRIPTION
  detailed multi-line description
OPTIONS
  explain/list parameters and arguments
EXAMPLES
  usage examples
SEE ALSO
  list related objects
AUTHOR
  author(s) + contact information
COPYRIGHT
  copyright/license info

VERSION               DATE       SOMETHING(SECTION)

Other possible sections would be ENVIRONMENT, EXIT STATUS, FILES, BUGS, or HISTORY.

Every man page belongs to a numbered section. CLI tools belong to section #1. The man command looks for matching man pages in each section in ascending order. If two man pages of the same name exist in different section, use man <section number> <name> to open the page in the desired section.

Let's create a minimal man page for a tool called noop . To format the page, the example uses “man macros”—short codes at the beginning of a line, starting with a dot. See man 7 man for more information about the macros syntax.

(Side note: BSD-based Unixes like Darwin prefer the mdoc markup language. If you run man 7 man there, the page claims that the man macros are a “legacy” formatting language.)

Our example page uses three marcos:

  • .TH [name of program] [section number] [center footer] [left footer] [center header] : This information will be shown in the header and footer of the page, respectively.
  • .SH [string] : A section
  • .TP : A “hanging tag”. The first line below .TP is indented normally, the following lines get an extra indent. Used for flag descriptions.

There are quite a few more macros available, but these three are enough to get started:

.TH "NOOP" "1" "December 08, 2024" "Noop User Manual"
.SH NAME
noop
.SH SYNOPSIS
noop [\-v] [\-a]
.SH Description
noop does nothing.
.SH OPTIONS
.TP
\-v
Verbose output.
.TP
\-a
Do absolutely nothing at all.
.SH AUTHORS
Christoph Berger.

Save the above text to a file named “noop.1” (the extension represents the section number) and pass it to the man command:

man ./noop.1

You should then get a page similar to this one:

NOOP(1)         General Commands Manual        NOOP(1)

NAME
       noop

SYNOPSIS
       noop [-v] [-a]

Description
       noop does nothing.

OPTIONS
       -v     Verbose output.

       -a     Do absolutely nothing at all.

AUTHORS
       Christoph Berger.

Noop User Manual   December 8, 2024            NOOP(1)

That was quite easy, wasn't it?

To “install” a man page,

  1. Copy it to /usr/local/share/man/man1/ (or the appropriate section)
  2. Ensure the copy has mode 0644
  3. gzip it

Two options (source: linux.org):

cp noop /usr/local/man/man1/noop.1 && chmod 0644 /usr/local/man/man1/noop.1 && gzip /usr/local/man/man1/noop.1

A shorter version, using the install command:

gzip noop.1 && install -g 0 -o 0 -m 0644 noop.1.gz /usr/local/man/man1/

Add these steps to the installer of your choice (such as goreleaser) or your favorite build tool.

2. Use Pandoc

Pandoc converts almost any open text format to almost any other open text format. You can make use of this to write your manpage in Markdown, with a few “extras” such as the pandoc_title_block or definition_lists extensions, that you can see in action in this sample Markdown manpage:

% NOOP(1) Noop User Manual
% Christoph Berger
% December 08, 2024

# NAME
noop

# SYNOPSIS

noop [-v] [-a]

# DESCRIPTION

noop does nothing.

# OPTIONS

-v
: Verbose output.

-a
: Do absolutely nothing at all.

Run this through pandoc and man to get the same man page as before:

pandoc -s -t man noop.md -o noop.1 && man ./noop.1

3. Use muesli/mango

muesli/mango generates man pages from Go flags or pflags, or from CLI frameworks like Cobra, Coral, or Kong.

Mango is a package, not a command, so you can make your CLI tool generate a man page on demand (for example, during the installation process.)

4. Use cpuguy83/go-md2man

Similar to Pandoc, the go-md2man tool converts Markdown files into manpage format. The exact Markdown format is not documented, but a peek into the raw version of go-md2man.1.md gives a few hints.

5. Use Cobra

The Cobra CLI framework features automated man page generation based on the data you would supply to the subcommand definitions anyway.

As of this writing, the exact way of generating a man page with Cobra is not documented.

6. Use lufia/godoc2man

Do you love to kill two birds with one stone? (Not literally, please!) Then godoc2man is the tool for you. Write the package documentation for package main with man-like sections, and godoc2man turns it into a man page.

Summary

CLI tools should follow certain standards, regardless of the language they're built with. Man pages are the Linux standard of documenting CLI tools, and the above list should contain a man page generation option for every taste.

Go build awesome tools!