What is a man page?

If you’ve ever typed man ls or man ssh, you’ve used one: a manual page. These pages are the canonical documentation of Unix—every command, system call, file format, or standard has its place.

They were created in 1971 at Bell Labs by Dennis Ritchie and Ken Thompson, after their manager insisted the system needed proper documentation. At first, this meant printed binders: plain text written in roff(7) typesetting macros, formatted for the line printer. It worked—but it was clunky. Finding the right entry meant flipping through a binder, and keeping it up to date meant re-printing and re-binding constantly.

The solution was man(1): a program that could render those same roff sources directly on a terminal screen. Suddenly, documentation was always current, always searchable, and always at your fingertips. Fifty years later, we’re still using it. Talk about an evergreen technology—manpages have outlasted punch cards, paper tape, floppy disks, and a host of other “modern” technologies.

It’s documented in The Book, somewhere…
– Larry Wall in 10502@jpl-devvax.JPL.NASA.GOV

A manpage today still comes in two forms: the source page (written in roff macros) and the formatted page (sometimes called a cat page), which is what man(1) actually displays.

Why is this cool?

Using man(1) is second nature, but writing your own page can feel intimidating enough that most of us skip it for our own scripts and tools. That’s a shame—because a proper manpage doesn’t just document your program, it legitimizes it. Nothing makes a tool feel more “real” than typing man mytool and seeing it pop up like it was always meant to live in /usr/bin.

In this post I’ll share the concepts that finally made the process click for me—and hopefully for you as well.

screenshot of man ./mytool.1
With a good reference and a simple template, you can give your code the kind of documentation that feels native to Unix itself.

The Quick and Dirty Howto

The manual system is dead simple:

  • View a page:
man ls
man ./mytool.1   # view local draft
  • Search all pages for a keyword:
man -k network
apropos network   # same thing
  • Search only in specific sections
man -s 2 -k open  # search section 2 (system calls) for open

Pro-Tip a really cool package worth installing is tldr which serves as a companion to the standard man pages and just shows practical usage examples

GNU vs BSD flavor

There are small differences depending on your platform. Linux typically ships the GNU man/groff toolchain, while macOS and the BSDs use their BSD versions. The basics are the same, but options and formatting quirks can differ.

The best way to check your system is with the manuals themselves

man man
man groff
man troff 

GUI manpage viewers

Most of us are used to paging through manpages in a terminal, but there are GUI front-ends that make them easier to browse, search, and bookmark.

On macOS you’ve got a few nice tricks:

  • Built-in x-man-page:// scheme:

    open x-man-page://ls
    

    Opens a dedicated manpage window with search and scroll.

  • Render as a PDF in Preview:

    man -t ls | open -f -a Preview
    
  • ManOpen — a classic NeXTStep-era tool with its openman helper.

  • Man Reader — modern, App Store version with tabs, bookmarks, search.

  • Bwana — browser-based, supports man:foo URIs.

On Linux, you’ll find:

  • yelp (GNOME Help Browser)
  • khelpcenter (KDE Help Center)
  • tkman (old-school but still works)
  • Some distros also support man:foo URIs in Firefox/Konqueror.

Check out my manfzf script for a fuzzy CLI manpage picker.


Pro Tip On both macOS and Linux, you can alias man to your favorite viewer:

alias man='openman'          # macOS with ManOpen
alias man='yelp man:'        # GNOME
alias man='khelpcenter man:' # KDE

Manpage navigation keys (less / man pager)

If you spend enough time in the manual you may find the following hotkeys useful:

Key Function
j Move forward one line. Prefix with a number to move that many lines (e.g., 6j moves forward six lines).
k Move back one line. Prefix with a number to move that many lines.
g Jump to the top of the manual.
G Jump to the end of the manual.
f Move forward one screen. (Space bar does the same.)
b Move back one screen.
d Move forward half a screen.
u Move back half a screen.
/pattern Search forward for pattern.
?pattern Search backward for pattern.
n Repeat the last search in the same direction.
N Repeat the last search in the opposite direction.
q Quit the manual pager.

Want to explore more search tricks like wrap-around, advanced pattern control, or highlighting tweaks? Check out the official less(1) manual: less — Linux manual page (search & navigation) :contentReference[oaicite:1]{index=1}


Manual sections (what those numbers mean)

You’ll often see references with a section number like foo(1) or bar(5)—that’s command_name(section).

Section Scope Examples
1 User commands (executables you run) ls(1), grep(1)
2 System calls (kernel entry points) open(2), write(2)
3 Library functions (libc, etc.) printf(3), malloc(3)
4 Special files (devices, drivers) null(4), random(4)
5 File formats & conventions passwd(5), crontab(5)
6 Games & demos fortune(6), nethack(6)
7 Misc (conventions, protocols, standards) man(7), regex(7), ascii(7)
8 System administration commands mount(8), systemctl(8)

Less common extras

  • 9 — Kernel routines (driver devs, low-level stuff)
  • l — Local (site-specific)
  • n — New (experimental)
  • o — Old (obsolete)
  • p — Public domain (rare)

⚡ Example: printf(1) is the shell command, printf(3) is the C library call. Use the number to disambiguate.


Anatomy of a manpage

serene photo of yellow wildflowers in a field
If things start to feel too cryptic, just think of the pretty flowers until it passes

At its core, a man page is written in roff, a plain-text markup language for formatting documents. In practice, almost everyone today uses groff (the GNU roff toolchain) to process these files.

Each line that starts with a dot (.) is a directive (called a macro), and everything else is plain text. For example:

  • .SH NAME → start a new section header
  • .B → make text bold
  • .TP → set up a hanging indent (used in option lists)

When you run man, the source file is passed through groff with the -man macro package. That expands those directives into the nicely formatted output you see in your terminal. A raw man page looks cryptic but predictable—it’s typesetting code waiting to be compiled into clean columns.

Common sections and macros

A man page is built with roff macros. Each section header is declared with the .SH macro, and specific formatting macros handle bold, italics, lists, and examples.

Section Purpose Macro(s)
NAME One-line description (name \- summary). Parsed by whatis(1). .SH NAME
SYNOPSIS Usage syntax: command, options, arguments. Typically uses bold for commands, italics for args. .SH SYNOPSIS, .B, .I, .RI
DESCRIPTION Longer explanation of what the tool does. .SH DESCRIPTION, text macros (.PP for paragraphs)
OPTIONS Command-line flags, usually formatted as a tagged list. .SH OPTIONS, .TP (tagged paragraph)
EXAMPLES Literal usage examples. .SH EXAMPLES, .EX / .EE (example block)
FILES Config or data files related to the program. .SH FILES, .I (italic filenames)
ENVIRONMENT Environment variables the program respects. .SH ENVIRONMENT, .TP for listing vars
EXIT STATUS Exit codes and their meanings. .SH EXIT STATUS, .TP
DIAGNOSTICS Explanation of error messages. .SH DIAGNOSTICS, .TP
BUGS Known bugs, limitations, or witty remarks. .SH BUGS
SEE ALSO Cross-references to related commands/libraries. .SH SEE ALSO, .BR foo (1) for bold+roman
AUTHOR Who wrote/maintains the program. .SH AUTHOR
COPYRIGHT Licensing information. .SH COPYRIGHT
HISTORY (optional) Historical notes on command evolution. .SH HISTORY

Handy Formatting Macros

  • .B text — bold (usually commands and flags)
  • .I text — italic (filenames, variables)
  • .RI arg1 arg2 — roman + italic (e.g., command arg)
  • .TP — tagged paragraph (great for options lists)
  • .EX / .EE — example block, keeps spacing/formatting
  • .BR foo (1) — bold + roman mix, used in SEE ALSO references

Rule of thumb: section headers (NAME, SYNOPSIS, OPTIONS, etc.) always get .SH. Within those, you mix .B, .I, .TP, etc. to get the formatting you want.


Minimal manpage template (customize me)

Let’s take a look at a minimal, complete manpage with helpful comments as a guide to creating your own

.\" ==========================================================
.\" mytool.1 — Minimal, complete manpage template
.\" How to view locally:
.\"   man ./mytool.1
.\"   groff -man -Tutf8 mytool.1 | less
.\"
.\" Conventions:
.\"   - Edit placeholders in ALL CAPS.
.\"   - Keep lines under ~80 cols if you can.
.\"   - Remove comments (lines starting with .\") when publishing.
.\" ==========================================================

.TH MYTOOL 1 "Aug 2025" "MyTool 1.0" "User Commands"
.\" .TH = Title Header: NAME SECTION DATE VERSION MANUAL-TITLE

.SH NAME
mytool \- one-line summary of what the tool does
.\" The dash must be escaped: \-   (this line is parsed by whatis(1)/apropos)

.SH SYNOPSIS
.B mytool
.RI [ OPTIONS ] " ARG1 " [ ARG2 ... ]
.\" Use .B for the command, .RI to mix roman/italic (nice for args).
.\" Keep synopsis concise; show common forms, not every permutation.

.SH DESCRIPTION
.B mytool
does X in order to achieve Y (1–3 sentences). State defaults and
side-effects briefly. Longer tutorials go in README or web docs.

.PP
Typical use cases:
.IP \[bu] 2
Do foo to bar quickly.
.IP \[bu] 2
Automate baz with a single command.

.SH OPTIONS
.TP
.B -h, --help
Show help and exit.
.TP
.B -v, --version
Print version and exit.
.TP
.B -o, --output \fIFILE\fR
Write output to \fIFILE\fR (default: stdout).
.TP
.B --dry-run
Print what would happen without making changes.

.SH EXAMPLES
.EX
# basic usage
$ mytool --output result.txt input.dat

# preview actions
$ mytool --dry-run input.dat

# multiple files
$ mytool -o out/ merged*.csv
.EE

.SH FILES
.I ~/.config/mytool/config.yml
Optional configuration file.
.PP
.I /var/log/mytool/mytool.log
Log file (if logging enabled).

.SH ENVIRONMENT
.TP
.B MYTOOL_CONFIG
Path to an alternate config file.
.TP
.B MYTOOL_DEBUG
Set to 1 for verbose diagnostics.

.SH EXIT STATUS
.TP
.B 0
Success.
.TP
.B 1
General error (see DIAGNOSTICS).
.TP
.B 2
Invalid arguments.

.SH DIAGNOSTICS
Common error messages and remedies:
.TP
.B "cannot open file"
Check permissions and that the path exists.
.TP
.B "unknown option"
See
.BR mytool (1)
under OPTIONS.

.SH SEE ALSO
.BR grep (1),
.BR awk (1),
.BR roff (7),
.BR help2man (1)

.SH AUTHOR
Your Name <you@example.com>

.SH COPYRIGHT
Copyright \(co 2025 Your Name.
License: MIT.

.SH VERSION
1.0.0

screenshot of man ./mytool.1
That creates a manpage with the familiar structure and conventions.

That’s all there is to it. Again it’s just plain text and some macros and escapes to control formatting.

Download If you would like to download that template as a ready to go stub, you can grab it Here


To view it without installing first, you can do the following:

# simplest: point man directly at it
man ./test.1

# or, explicitly render with groff for debugging layout
groff -man -Tutf8 ./test.1 | less

# on macOS, you can preview it in Preview.app as a PDF:
man -t ./test.1 | open -f -a Preview

Using help2man to automatically generate documentation for your scripts

help2man is a little utility that auto-generates manpages from the output of your program’s --help and --version flags. Instead of hand-crafting a .1 file with groff macros, you can let your script speak for itself, and help2man will translate that into a standard UNIX manual page.

It’s lightweight, doesn’t need you to write groff, and is often bundled in Debian packaging workflows for quick manpages. The downside, I’ve found, is that help2man produces relatively ugly output regardless of how good your --help output is. You’ll probably find yourself polishing it to make it look nice but it does produce reasonable enough output.


Requirements

To work with help2man, your script or program must support:

  • -h / --help → prints a usage summary and available options
  • -v / --version → prints the program name and version
  • the script must be executable

That’s all help2man needs to create a basic manual page.

Pro-Tip: Keep your –help text tidy and descriptive—help2man copies those lines directly into the man page. If your help is garbage, your man page will be, too.


Minimal script example

Here’s a tiny, example Python script that satisfies those conditions:

#!/usr/bin/env python3
import sys

NAME = "myscript"
VERSION = "1.0"

def main():
    if "-h" in sys.argv or "--help" in sys.argv:
        print(f"Usage: {NAME} [options]\n")
        print("Options:")
        print("  -h, --help     Show this help message")
        print("  -v, --version  Show version info")
        sys.exit(0)

    if "-v" in sys.argv or "--version" in sys.argv:
        print(f"{NAME} {VERSION}")
        sys.exit(0)

    print("Hello, world!")

if __name__ == "__main__":
    main()

Make it executable:

chmod +x myscript

Generating the manpage

Run help2man on your script:

help2man ./myscript > myscript.1

Preview it with:

man ./myscript.1

Optional polish

You can add extra details with flags:

help2man --name="Tiny demo script" --section=1 ./myscript > myscript.1

And install it for local use:

sudo install -m 0644 myscript.1 /usr/local/share/man/man1/
sudo mandb 2>/dev/null || true
man myscript

📦 Installing a manpage locally

After you’ve got a .1 file you’re happy with, install it into the system manpath so man mytool works like any built‑in command. Section 1 pages usually live under /usr/local/share/man/man1/.

# 1) Copy your page into section 1
sudo install -m 0644 mytool.1 /usr/local/share/man/man1/

# 2) Rebuild the whatis/apropos database
# Linux:
sudo mandb

# macOS (BSD mandoc):
sudo makewhatis /usr/local/share/man
# (Tip: `man makewhatis` for details)

Verify:

# Where will man find it?
man -w mytool

# Does whatis/apropos see it?
whatis mytool  # or: apropos mytool

# Use it!
man mytool

User‑local install (no sudo)

You can keep manpages in your home directory:

# Put it in your personal manpath:
install -d "$HOME/.local/share/man/man1"
install -m 0644 mytool.1 "$HOME/.local/share/man/man1/"

# Point man(1) at it for this shell:
export MANPATH="$HOME/.local/share/man:$(manpath 2>/dev/null || echo /usr/share/man:/usr/local/share/man)"

# Or scope just one lookup:
man -M "$HOME/.local/share/man" mytool

Troubleshooting quickies

# See your active manpath (Linux):
manpath

# On macOS, print the search path:
man -w

# If apropos/whatis doesn’t show your entry yet:
sudo mandb                         # Linux
sudo makewhatis /usr/local/share/man  # macOS

# Check the NAME line format (parsed by whatis):
# must be: "name \- one line summary" (note the escaped dash \-)
grep -n '^\.SH NAME' -n mytool.1 -n

man page on wikipedia help2man Reference Manual

Conclusion

“Whoever undertakes to set himself up as a judge of Truth and Knowledge is shipwrecked by the laughter of the gods.” – Albert Einstein

That’s it for now. Manpages can feel ancient and mysterious, but they’re still one of the most reliable ways to give your tools polish and permanence. With just a text file and a few macros, you can drop your work into the same lineage as ls(1) and grep(1).

If you’ve got a script you use every day, give it a manpage. Start small — a NAME, a SYNOPSIS, maybe a couple of OPTIONS. The next time you type man myscript, you’ll see your own work sitting comfortably alongside fifty years of Unix history.

💡 Got a favorite trick, or did this inspire you to write a wild cheatsheet manpage? Did you find yourself thinking of the pretty flowers? I’d love to hear it: feedback@adminjitsu.com.