Fix Man Command Not Working on Arch Linux (WSL2)

July 2024

Preface

To my surprise, on my unofficial installation of Arch Linux on WSL2, the man command wasn’t working. It couldn’t find any man pages. I double checked to make sure I properly installed man accordingly to my distro.

# NOTE: man-db implements man on Arch Linux.
pacman -Sy man-db man-pages

Troubleshooting

Symptoms

When you try to access the manual for a specific command it’ll output the following message.

No manual entry for man
See 'man 7 undocumented' for help when manual pages are not available.

In this case I was trying to access the manual pages for the man command. The message itself indicates manual pages couldn’t be found for that command, but this is a larger issue when you’re expecting them to exist for commonly installed binaries such as ls, cat, or vi.

Narrowing Down the Focus

The behavior of man is defined by the /etc/man_db.conf configuration file. At runtime it directs man to search all directories indicated by the MANDATORY_MANPATH directive.

MANDATORY_MANPATH       /usr/man
MANDATORY_MANPATH       /usr/share/man
MANDATORY_MANPATH       /usr/local/share/man

These directories were either non-existent or empty. Maybe our package manager is not installing them?

Running pacman-conf will output the contents of the pacman package manager configuration file located at /etc/pacman.conf.

[options]
RootDir = /
DBPath = /var/lib/pacman/
CacheDir = /var/cache/pacman/pkg/
HookDir = /etc/pacman.d/hooks/
GPGDir = /etc/pacman.d/gnupg/
LogFile = /var/log/pacman.log
HoldPkg = pacman
HoldPkg = glibc
NoExtract = usr/share/help/*
NoExtract = !usr/share/help/en*
NoExtract = !usr/share/help/C/*
NoExtract = usr/share/gtk-doc/html/*
NoExtract = usr/share/doc/*
NoExtract = usr/share/locale/*
NoExtract = usr/share/X11/locale/*
NoExtract = usr/share/i18n/*
NoExtract = !*locale*/en*/*
NoExtract = !usr/share/i18n/charmaps/UTF-8.gz
NoExtract = !usr/share/*locale*/locale.*
NoExtract = !usr/share/*locales/en_??
NoExtract = !usr/share/*locales/i18n*
NoExtract = !usr/share/*locales/iso*
NoExtract = !usr/share/*locales/trans*
NoExtract = !usr/share/X11/locale/C/*
NoExtract = !usr/share/X11/locale/compose.dir
NoExtract = !usr/share/X11/locale/iso8859-1/*
NoExtract = !usr/share/*locales/C
NoExtract = !usr/share/*locales/POSIX
NoExtract = !usr/share/i18n/charmaps/ANSI_X3.4-1968.gz
NoExtract = usr/share/man/*
NoExtract = usr/share/info/*
NoExtract = usr/share/vim/vim*/lang/*
NoExtract = etc/pacman.conf
NoExtract = etc/pacman.d/mirrorlist
Architecture = x86_64
VerbosePkgLists
NoProgressBar
ParallelDownloads = 5
CleanMethod = KeepInstalled
SigLevel = PackageRequired
SigLevel = PackageTrustedOnly
SigLevel = DatabaseOptional
SigLevel = DatabaseTrustedOnly
LocalFileSigLevel = PackageOptional
LocalFileSigLevel = PackageTrustedOnly
[core]
Usage = All
Server = https://geo.mirror.pkgbuild.com/core/os/x86_64
Server = https://mirror.rackspace.com/archlinux/core/os/x86_64
Server = https://mirror.leaseweb.net/archlinux/core/os/x86_64
[extra]
Usage = All
Server = https://geo.mirror.pkgbuild.com/extra/os/x86_64
Server = https://mirror.rackspace.com/archlinux/extra/os/x86_64
Server = https://mirror.leaseweb.net/archlinux/extra/os/x86_64

The NoExtract=file... directive will instruct pacman to never extract the specified files from a package into the filesystem. It’s useful when you don’t want a part of a package to be installed. In this case, NoExtract = usr/share/man/* prevents the man files we need.

Solution

Edit the pacman configuration file located at /etc/pacman.conf to comment out the line[1].

#
# /etc/pacman.conf
#
# See the pacman.conf(5) manpage for option and repository directives

#
# GENERAL OPTIONS
#
[options]
# The following paths are commented out with their default values listed.
# If you wish to use different paths, uncomment and update the paths.
#RootDir     = /
#DBPath      = /var/lib/pacman/
#CacheDir    = /var/cache/pacman/pkg/
#LogFile     = /var/log/pacman.log
#GPGDir      = /etc/pacman.d/gnupg/
#HookDir     = /etc/pacman.d/hooks/
HoldPkg     = pacman glibc
#XferCommand = /usr/bin/curl -L -C - -f -o %o %u
#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u
#CleanMethod = KeepInstalled
Architecture = auto

# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
#IgnorePkg   =
#IgnoreGroup =

#NoUpgrade   =
#NoExtract   =

# Misc options
#UseSyslog
#Color
NoProgressBar
# We cannot check disk space from within a chroot environment
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5

# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.
SigLevel    = Required DatabaseOptional
LocalFileSigLevel = Optional
#RemoteFileSigLevel = Required

# NOTE: You must run `pacman-key --init` before first using pacman; the local
# keyring can then be populated with the keys of all official Arch Linux
# packagers with `pacman-key --populate archlinux`.

#
# REPOSITORIES
#   - can be defined here or included from another file
#   - pacman will search repositories in the order defined here
#   - local/custom mirrors can be added here or in separate files
#   - repositories listed first will take precedence when packages
#     have identical names, regardless of version number
#   - URLs will have $repo replaced by the name of the current repo
#   - URLs will have $arch replaced by the name of the architecture
#
# Repository entries are of the format:
#       [repo-name]
#       Server = ServerName
#       Include = IncludePath
#
# The header [repo-name] is crucial - it must be present and
# uncommented to enable the repo.
#

# The testing repositories are disabled by default. To enable, uncomment the
# repo name header and Include lines. You can add preferred servers immediately
# after the header, and they will be used before the default mirrors.

#[core-testing]
#Include = /etc/pacman.d/mirrorlist

[core]
Include = /etc/pacman.d/mirrorlist

#[extra-testing]
#Include = /etc/pacman.d/mirrorlist

[extra]
Include = /etc/pacman.d/mirrorlist

# An example of a custom package repository.  See the pacman manpage for
# tips on creating your own repositories.
#[custom]
#SigLevel = Optional TrustAll
#Server = file:///home/custompkgs

[options]
NoExtract  = usr/share/help/* !usr/share/help/en* !usr/share/help/C/*
NoExtract  = usr/share/gtk-doc/html/* usr/share/doc/*
NoExtract  = usr/share/locale/* usr/share/X11/locale/* usr/share/i18n/*
NoExtract  = !*locale*/en*/* !usr/share/i18n/charmaps/UTF-8.gz !usr/share/*locale*/locale.*
NoExtract  = !usr/share/*locales/en_?? !usr/share/*locales/i18n* !usr/share/*locales/iso*
NoExtract  = !usr/share/*locales/trans*
NoExtract  = !usr/share/X11/locale/C/*
NoExtract  = !usr/share/X11/locale/compose.dir !usr/share/X11/locale/iso8859-1/*
NoExtract  = !usr/share/*locales/C !usr/share/*locales/POSIX !usr/share/i18n/charmaps/ANSI_X3.4-1968.gz
# NoExtract  = usr/share/man/* usr/share/info/*
NoExtract  = usr/share/vim/vim*/lang/*
NoExtract  = etc/pacman.conf etc/pacman.d/mirrorlist

Installing Missing Man Pages

Unfortunately, our non-existent manual pages don’t magically exist after doing this. We’ll need to re-install man-pages again, but there’s a catch.

Installing man-pages provides the manual pages for Linux, but not the other packages we’ve installed on the system. Running the following command will relieve that issue:

pacman -Qqo /usr/share/man | pacman -S -

Breaking Down the Command

The first half, pacman -Qqo /usr/share/man, lists all the packages that own files in /usr/share/man (i.e., packages related to manual pages).

It’ll then | (pipe) this list of package names to pacman -S - (which reads from stdin) and re-install those packages.