podlinkcheck -- check Perl pod L<> link references


 podlinkcheck [--options] file-or-dir...


The command line options are


Print a command line summary.

-I dir

Add an extra directory to look for target modules.


Print more about program operation (including CPAN loading).


Print the program version number and exit.


PodLinkCheck parses Perl POD from a script, module or documentation and checks that L<> links within it refer to a known program, module, or man page.

    L<foo>          check module, pod or program "foo"
    L<foo/section>    and check section within the pod
    L<bar(1)>       check man page "bar(1)"

The command line is either individual files or whole directories. For a directory all the .pl, .pm and .pod files under it are checked. So for example to churn through all installed add-on modules,

    podlinkcheck /usr/share/perl5

Bad links are usually typos in the module name or section name, or sometimes L<display|target> parts the wrong way around. Occasionally there may be an L<foo> used where just markup C<> or I<> was intended.


External links are checked by seeking the target .pm module or .pod documentation in the @INC path (per Pod::Find), or seeking a script (no file extension) in the usual executable PATH. A section name in a link is checked by parsing the POD in the target file.

If a module is not installed in @INC or extra -I directories then its existence is also checked in the CPAN indexes with App::cpanminus, CPAN::SQLite, CPAN or CPANPLUS. Nothing is downloaded, just current data consulted. A warning is given if a section name in a link goes unchecked because it's on CPAN but not available locally.

If checking your own work then most likely you will have copies of cross-referenced modules installed (having compared or tried them). In that sense the CPAN index lookups are a fallback.

Manpage links are checked by asking the man program if it recognises the name, including any number part like chmod(2). A manpage can also satisfy what otherwise appears to be a POD link with no sub-section, since there's often some confusion between the two.

Internal links are sometimes written

    L<SYNOPSIS>                     # may be ambiguous

but the Perl 5.10 perlpodspec advice is to avoid ambiguity between an external module and a one-word internal section by writing a section with / or quotes,

    See L</SYNOPSIS> above.         # good

    See L<"SYNOPSIS"> above.        # good

podlinkcheck warns about L<SYNOPSIS> section links. But not if it's both an valid external module and internal section -- because it's not uncommon to have a module name as a heading or item and an L<> link still meaning the external one.

Section Name Matching

An L<> section name can use just the first word of an item or heading. This is how Pod::Checker behaves and it's good for perlfunc cross references where just the function name can be given without the full argument list of the =item. Eg.


The first word is everything up to the first whitespace. This doesn't come out very well on a target like =item somefun( ARG ), but it's how Pod::Checker 1.45 behaves. If the targets are your own then you might make the first word or full item something sensible to appear in an L<>.

If a target section is not found then podlinkcheck will try to suggest something close, eg. differing only in punctuation or upper/lower case. Some of the POD translators may ignore upper/lower case anyway, but it's good to write an L<> the same as the actual target. no section "constructor" in "CHI"
      (file /usr/share/perl5/
      perhaps it should be "CONSTRUCTOR"

For reference, numbered =item section names go in an L<> without the number. This is good since the numbering might change. If podlinkcheck suggests a number in a target then it may be a mistake in the target document. A numbered item should have the number alone on the =item and the section name as the next paragraph.

    =item 1.                        # good

    The First Thing                 # the section name

    Paragraph about this thing.

    =item 2. The Second Thing       # bad

    Paragraph about this next thing.

The second item "2. The Second Thing" is not a numbered item for POD purposes, but rather text that happens to start with a number. Of course sometimes that's what you want, eg.

    =item 64 Bit Support

podlinkcheck uses Pod::Simple for parsing and so follows its interpretation of the various hairy L<> link forms. If an L<> appears to be mis-interpreted you might rewrite or add some escapes (like E<sol>) for the benefit of all translators which use Pod::Simple. In Perl 5.10 that includes the basic pod2man.

Other Ways to Do It

podchecker (the Pod::Checker module) checks internal links, but it doesn't check external links.

Test::Pod::LinkCheck is similar in a .t test framework. It uses some of PodLinkCheck but different reporting and a stricter approach to dubious POD.


Exit is 0 for no problems found, or non-zero for problems.



The search path for installed scripts.


Used by the various CPAN modules for ~/.cpan etc directories.


The usual extra Perl module directories (see "ENVIRONMENT" in perlrun), which become @INC where link targets are sought.


App::cpanminus is checked first since it's a bsearch of 02packages.details.txt, and CPAN::SQLite second since it's a database lookup. But if a target is not found there then the full CPAN and CPANPLUS caches are loaded and checked. This might use a fair bit of memory for a non-existent target, but it's also possible they're more up-to-date.

No attempt is made to tell which of the indexes is the most up-to-date. If a module has been renamed (bad) then it may still exist in an old index. The suggestion is to avoid having old stuff lying around (including old mirror files in App::cpanminus).

The code consulting may need a tolerably new version of that module, maybe 1.61 circa Perl 5.8.0. On earlier versions its index is not used.

The line:column number reported for an offending L<> is found by some gambits extending what Pod::Simple normally records. There's a chance it could be a little off within the paragraph.

Pod::Simple prior to version 3.24 didn't allow dots "." in man-page names, resulting in for example login.conf(5) being treated as a Perl module name not a man page name. If you have such links then use Pod::Simple 3.24 up.

Directories are currently traversed using File::Find::Iterator. It follows symlinks but neither its version 0.4 nor PodLinkCheck guard against infinite descent into symlink cycles. The intention perhaps would be follow all symlinks to files, but follow to a directory just once as protection against cycles.


~/.cpanm/sources/*/02packages.details.txt files from App::cpanminus

~/.cpan/cpandb.sql used by CPAN::SQLite

~/.cpan/Metadata used by CPAN

~/.cpanplus/* variously used by CPANPLUS


podchecker, podlint

Pod::Simple, Pod::Find, CPAN, CPAN::SQLite, CPANPLUS, cpanm

Test::Pod::LinkCheck, Pod::Checker, Test::Pod



Copyright 2010, 2011, 2012, 2013, 2016, 2017 Kevin Ryde

PodLinkCheck is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version.

PodLinkCheck is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with PodLinkCheck. If not, see <>.