Writing Linux man pages & Keeping A Personal Collection of man pages

The master version of this file resides at /home/paulgorman/repo/man/README. A public copy resides at

Linux man pages are produced by groff, a GNU clone of the venerable Unix typesetting system troff. Groff is a full-fledged typesetting system. Man pages use a particular set of groff macros, as described in groff_man(7). Groff instructions appear on their own line, beginning with a dot.

Note that FreeBSD and OpenBSD have adopted a set of macros more semantically specific to man pages called “mdoc” or “mandoc”. See groff_mdoc(7) and mdoc.samples(7).

Custom/personal man pages (Method 1)

If we create ~/man/, manpath should find it automatically. See manpath(1) and /etc/manpath.config.

Put man files in section directories, like ~/man/man1/. The files need not be gzipped; ~/man/man1/foo.1 is fine. To avoid collisions with system man pages, customize the section, like ~/man/man1/ls.1m (the “m” is arbitrary).

View the custom man page like man 1m foo. For man to find personal pages first, add the custom section (e.g. “1m”) at the start of SECTION in /etc/manpath.config.

Run mandb to update the database used for things like whatis.

Custom/personal man pages (Method 2)

We keep our custom man pages wherever we want (e.g. $HOME/repo/man/), and use a custom shell script, like:

# Check for local/personal man pages


# If we want custom sections like $MANPATH/manfoo/, set
# $personal_man_sections as a colon-delimited list:
# personal_man_sections='foo:paul:99'

# Colorize man pages
man() {
	env LESS_TERMCAP_mb=$'\E[01;31m' \
	LESS_TERMCAP_md=$'\E[01;38;5;74m' \
	LESS_TERMCAP_me=$'\E[0m' \
	LESS_TERMCAP_se=$'\E[0m' \
	LESS_TERMCAP_so=$'\E[30;47m' \
	LESS_TERMCAP_ue=$'\E[0m' \
	LESS_TERMCAP_us=$'\E[04;38;5;146m' \
	man "$@"

if [[ -v $personal_man_sections ]]
		man --section=${personal_man_sections} --manpath=${personal_man_path} $@ 2> /dev/null
		man --manpath=${personal_man_path} $@ 2> /dev/null

# If didn't find a custom man page, check the regular man path and sections:
if [[ $? -ne 0 ]]
		man $@

It still may be desirable to do ln -s ~/repo/man ~/man, so that mandb -u makes whatis foo find our custom pages.

Excerpts from man(1), /etc/manpath.config, man-pages(7), man(7), and groff_man(7)

man(1) - an interface to the on-line reference manuals (somewhat paraphrased):

Each page argument given to man is normally the name of a program, utility or function.
A  section, if provided, will direct man to look only in that section of the manual.
The default action is to search in all of the available sections following a pre-defined order
("1 n l 8 3 2 3posix 3pm 3perl 3am 5 4 9 6 7" by default, unless overridden by the
SECTION directive in /etc/manpath.config), and to show only the first page 
found, even if page exists in several sections.

The table below shows the section numbers of the manual:

1   Executable programs or shell commands
2   System calls (functions provided by the kernel)
3   Library calls (functions within program libraries)
4   Special files (usually found in /dev)
5   File formats and conventions eg /etc/passwd
6   Games
7   Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7)
8   System administration commands (usually only for root)
9   Kernel routines [Non standard]

The following conventions apply to the SYNOPSIS section and can be used as a guide in other sections.

bold text          type exactly as shown.
italic text        replace with appropriate argument.
[-abc]             any or all arguments within [ ] are optional.
-a|-b              options delimited by | cannot be used together.
argument ...       argument is repeatable.
[expression] ...   entire expression within [ ] is repeatable.

    If $MANSECT is set, its value is a colon-delimited list of sections
    and it is used to determine which manual sections to search and in
    what order.  The default is "1 n l 8 3 2 3posix 3pm 3perl 3am 5 4  9  6  7",
    unless overridden by the SECTION directive in /etc/manpath.config.

From /etc/manpath.config:

# Section names. Manual sections will be searched in the order listed here;
# the default is 1, n, l, 8, 3, 0, 2, 5, 4, 9, 6, 7. Multiple SECTION
# directives may be given for clarity, and will be concatenated together in
# the expected way.
# If a particular extension is not in this list (say, 1mh), it will be
# displayed with the rest of the section it belongs to. The effect of this
# is that you only need to explicitly list extensions if you want to force a
# particular order. Sections with extensions should usually be adjacent to
# their main section (e.g. "1 1mh 8 ...").
SECTION         1 n l 8 3 2 3posix 3pm 3perl 3am 5 4 9 6 7

man-pages(7) - conventions for writing Linux man pages (somewhat paraphrased):

Conventions for source file layout
    Limit source code line length to 75 characters where possible
    to avoid line-wrapping when patches emailed inline.
    New sentences should be started on new lines;
    This makes it easier to see the effect of patches.

Title Line
The first command in a man page should be a TH command:
      .TH title section date source manual
       title     The title of the man page in all caps (e.g., MAN-PAGES).
       section   The section number (e.g., 7).
       date      The date of the last nontrivial change made to the man page in the form YYYY-MM-DD.
       source    The source of the command, function, or system call (e.g., GNU or Linux)
       manual    The title of the manual (e.g., Linux Programmer's Manual).

Sections within a manual page:
NAME    e.g., "ls - list directory contents"
SYNOPSIS    Syntax of the command and its argument; 
            boldface for as-is text, and italics for replaceable arguments;
            brackets [] surround optional arguments;
            pipes | separate choices;
            ellipses ... can be repeated.
CONFIGURATION  (Normally only for section 4, i.e., special files like in /dev.)
DESCRIPTION    What it does.
OPTIONS        Command-line options (for sections 1 and 8).
EXIT STATUS    Possible exit values and their causes (sections 1 and 8).
RETURN VALUE   (For sections 2 and 3)
ERRORS         (For sections 2 and 3)
ENVIRONMENT    List of environment variables that affect the program and how.
FILES          List of configuration, startup, and files on which the program directly operates.
VERSIONS       (For sections 2 and 3)
ATTRIBUTES     (For sections 2 and 3)
CONFORMING TO  Describe any related standards or conventions (e.g., POSIX).
NOTES          Miscellaneous notes.
BUGS           Limitations, known defects, inconveniences.
EXAMPLE        One or more examples demonstrating how it's used.
AUTHORS        List of authors; omission of this section is recommended.
SEE ALSO       Alphabetical list of related man pages, or possibly other sources.

    For hyperlinks, use the .UR/.UE macro pair (see groff_man(7)).
    This produces proper hyperlinks that can be used in a web browser, when rendering a page with, say:
        $ man -Hfirefox-esr pagename

Real minus character
    For negative number or options with a leading dash (e.g. "ls -l"), escape the dash:
        ls \-l

man(7) - macros to format man pages (somewhat paraphrased):

.\" text
    This is a comment.

The only mandatory heading is NAME.
    .SH NAME
    foo \- my brief description
It is important to follow this format, and use a backslash before the single dash which follows the name.
This is used by mandb(8) to create a database of short descriptions for whatis(1) and apropos(1).

groff_man(7) - groff man macros to support generation of man pages (somewhat paraphrased):

This section describes the available macros for manual pages.
.EE  Example/End Example
.HP [nnn]
    Hanging paragraph. Paragraph with hanging left indent (indentation set to nnn).
.IP [designator] [nnn]
    Indented paragraph. Paragraph, with designator marking its beginning, indented nnn.
.P  Mutual aliases. A line break, followed by a vertical space downward.
.RE [nnn]
    Restore the previous left margin, or move it left by nnn.
.RS [nnn]
    Move left margin to the right by nnn.
.SH [text for heading]
    An unnumbered section heading sticking out to the left (indentation of following text is reset to default).
.SS [text for heading]
    A secondary unnumbered section heading.
.TH title section [extra1] [extra2] [extra3]
    Set title of the man page and the section.
    The section must hav ea value between 1 and 8, with an optional string appended (e.g. ".pm").
.TP [nnn]
    Paragraph with a label, indented to nnn.
    The first line following this macro is used at the label.
    A header/label continuation for a .TP macro.
    (For example, the .LP, .PP, and .P labels above.)

.B [text]
    Bold face [text] or the following line.
.BI text
    Alternate bold face and italic text.
        .BI this "word and" that
    renders 'this' and 'that' as bold, but 'word and' as italic.
.BR text
    Alternate bold face and roman.
.I [text]
    Italic face [text] or the following line.
.IB text
    Alternate italic and bold.
.IR text
    Alternate italic and roman.
.RB text
    Alternate roman and bold.
.RI text
    Alternate roman and italic
.SB [text]
    Text on this line or next in bold, one point size smaller than the default font size.
.SM [text]
    Text on this line or next one point size smaller than the default font size.

.MT address
.ME [punctuation]
    Email address. Text following the address, until .ME is the name:
        Paul Gorman
.UR url
.UE [punctuation]
    Web link.

.OP key value
    Optional argument. Rendered surrounded by brackets [].
.SY command
    Begin synopsis. Rendered with hanging indent.
    Ends synopsis and restored normal indentation.

    .SY foo
    .OP \-asdf
    .OP \-g bar
    .OP \-y bas
    .OP \-z bam
    .RI [ file
    .IR .\|.\|. ]


/usr/share/man/man2/fcntl.2.gz (suggested as canonical example by man-pages(7))

When editing groff, if vim doesn’t do syntax highlighting, :set syntax=groff.


.\" Margin notes about ls
.TH LS 1m 2015-06-15 "Linux" "Marginalia"
ls \- margin notes about
.B ls [options]
.B ls \-cr1p
List new files at bottom (i.e. by ctime, reversed, single\-column, append
slash to directories).
.B ls \-i
List by inode. Handy for removing weirdly named files like:
    find \-inum 17175729  \-exec rm \-i {} \e;
Paul Gorman