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.

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

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

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
Links and stuff
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.