Home / Alt manpages / perlstyle(1)

  • perlstyle(1)
  • User command
  • linux

Make Perl Code Easier to Review with perlstyle

You will turn the main advice from perlstyle(1) into a small working Perl script: strict checking and warnings enabled, readable layout, explicit error handling, and a syntax check you can repeat before review. The examples target Perl 5.38.2 and the perl-doc package installed on this machine. The manpage is a style guide, not a formatter, so the work is choosing and checking conventions rather than running a rewriter.

Allow about 20 minutes for the example and a first pass over an existing script. You need a shell, Perl, and a text editor. Everything below runs as your normal user and writes only a file in a temporary working directory. No sudo, service restart, module installation, or global configuration is required.

1. Confirm the local Perl guidance

Read the installed manual and record the interpreter version before adopting its version-specific opening pattern:

$ man perlstyle
$ perl -v | sed -n '1,4p'
This is perl 5, version 38, subversion 2 (v5.38.2)

The installed manual recommends use v5.36; or newer as a concise way to enable both strict and warnings, along with other named features. That is a version requirement for the script. If your deployment still supports an older Perl, do not copy it blindly: use the compatibility policy for that deployment and enable the pragmata explicitly if appropriate.

Checkpoint

You know which interpreter will run the code and whether the requested Perl version is acceptable for your target machines.

2. Start with checks that fail early

Create a temporary example. The program reads a directory path from its first argument, counts ordinary directory entries, and reports system-call failures with the path and Perl's operating-system error:

$ workdir=$(mktemp -d)
$ cd "$workdir"
$ mkdir sample
$ touch sample/one.txt sample/two.txt
$ editor count_entries.pl

Put this in count_entries.pl:

use v5.36;

my $directory = $ARGV[0] // die "usage: $0 DIRECTORY\n";

opendir(my $handle, $directory)
    or die "can't opendir $directory: $!\n";

my @entries = grep { $_ ne '.' && $_ ne '..' } readdir($handle);
closedir($handle)
    or die "can't closedir $directory: $!\n";

say "$directory: " . scalar(@entries) . " entries";

The explicit use v5.36; line enables the strict and warnings pragmata for this example. The manual warns against relying on the older -w switch or the $^W variable: those settings can affect code you did not write, including modules. The or die checks also matter. A failed system call should identify the operation and include $!, so the operating system's reason is not discarded.

Run the script with a known directory:

$ perl count_entries.pl sample
sample: 2 entries

Try a missing path as well. This is safe and demonstrates the useful failure boundary:

$ perl count_entries.pl missing
can't opendir missing: No such file or directory

The wording after the colon depends on the operating system. The non-zero exit status is the part a caller should rely on.

3. Make control flow visible

Prefer the form that makes the main action obvious. A trailing conditional is useful when the action is still the sentence's subject:

print "starting analysis\n" if $verbose;

Do not hide that action behind a punctuation expression merely to save a line:

$verbose && print "starting analysis\n";

For error handling, the manual similarly favours open(...) or die ... over putting the important operation inside an unless modifier. These are style choices, not a claim that Perl cannot execute the shorter alternatives. Choose the form that lets a reviewer find the operation and its failure path quickly.

When a loop needs to stop in the middle, label the loop and use last or next instead of rearranging the body into a harder-to-read shape:

LINE:
for my $line (<STDIN>) {
    chomp $line;
    last LINE if $line eq 'STOP';
    next LINE if $line =~ /^#/;
    print "$line\n";
}

Labels are ordinary Perl syntax. Use them when they explain which loop a transfer belongs to, especially with nested loops. Keep the label and its loop close together so the relationship is visible.

4. Use names and layout that carry meaning

Four-column indentation, spaces around most operators, a space after commas, and blank lines between separate jobs give the reader a predictable shape. Put an opening brace beside the construct where possible, and align the closing brace with the keyword that opened the block:

sub readable_name {
    my ($input_path, $verbose) = @_;

    my $result = process_file($input_path);
    print "processed $input_path\n" if $verbose;
    return $result;
}

Use mnemonic names. Short names can be fine in a small scope, but longer identifiers are easier to scan when words are separated with underscores, such as $input_path. The manpage suggests case as an additional signal: lower-case names for function-scope variables, mixed case for package-wide values, and capitals for constants. Treat that as a convention, not a substitute for a descriptive name.

Function and method names are generally clearer in lower case, with parentheses when they are being discussed as calls. Package names are an exception: non-pragma modules conventionally start with a capital letter and use mixed case. Do not turn this into a mechanical renaming exercise. Preserve public interfaces and make one small, reviewable change at a time.

5. Simplify difficult expressions without changing behaviour

Parentheses can remove ambiguity when several list operators or defaults are involved. Compare a compressed expression whose grouping a reader must reconstruct with one that shows the intended order:

my @names = sort { lc($a) cmp lc($b) } @input_names;
my $first_name = $names[0];

For a regular expression that has become line noise, use the /x modifier and lay out its parts with comments or whitespace. Choose a delimiter that does not compete with the expression's own slashes or backslashes:

my $valid = $value =~ m{
    \A [a-z0-9_]+ \z
}x;

The modifier changes how literal whitespace in the pattern is interpreted, so test the expression after expanding it. Do not use grep, map, or backticks only to throw away their return values. If the task is iteration, use for or foreach; if it is an external command, use an explicitly checked system call with a carefully defined argument list.

6. Check, test, and review the result

First ask Perl to compile the file without running it:

$ perl -c count_entries.pl
count_entries.pl syntax OK

Then run the normal case and the failure case again. A syntax check does not prove that directory permissions, input data, external commands, or portability assumptions are correct. Test those behaviours separately.

When code uses a feature that may not exist on every target, the manual suggests testing it in an eval or checking the running Perl version. Keep the compatibility test near the feature it protects, and document the minimum version rather than making readers infer it from scattered syntax. For a script that requires Perl 5.36, the use v5.36; declaration already makes an older interpreter fail early.

For reusable code, consider whether a one-off script should become a module or object class, and document it with consistent Perl POD. The manual's POD advice is practical: mark functions, variables and module names as code, and make command and file names recognisable. Above all, be consistent within the project. Consistency reduces the number of local rules a reviewer must hold in mind.

Checkpoint

Compilation passes, the intended output is reproduced, and a deliberately bad input produces a useful non-zero failure.

Done means

  • The script declares its required Perl version and gets strict checking and warnings through that declaration.
  • System calls check their return values and report the failed operation with $!.
  • Control flow, loop exits, names, indentation, and difficult expressions are readable without relying on clever defaults.
  • perl -c reports syntax OK, and normal and failure cases have both been exercised.
  • No elevated command or persistent system change was needed. To remove this temporary example, run rm -rf "$workdir" only while $workdir still contains the path printed by mktemp.