Home / Alt manpages / perlform(1)

  • perlform(1)
  • User command
  • linux

Build Fixed-Width Perl Reports with format and write

You will finish with a small Perl report that prints aligned text, numbers and a wrapped description, using Perl's built-in format and write features. The examples match Perl v5.38.2 and the perl-doc package installed here as version 5.38.2-3.2ubuntu0.6.

Allow about fifteen minutes. You need Perl and a text editor. Check the version with perl -v. This guide only writes a file in the directory you choose and prints to standard output; it does not need elevated privileges or change system configuration.

1. Create a minimal report

Start with one format named STDOUT, which is also the default format for the standard output filehandle. A format is declared between a line beginning with format and a line containing a single dot in column 1. Save this as /tmp/perl-report.pl:

#!/usr/bin/perl
use strict;
use warnings;

my ($name, $team, $total) = ('Ada Lovelace', 'Research', 42);

format STDOUT =
Name:  @<<<<<<<<<<<<<<<<<<<<  Team: @<<<<<<<<<<  Total: @###
       $name,                         $team,             $total
.

write;

The picture line contains literal labels and fields. Each @ starts a regular field. Repeated < characters make a left-justified text field, while repeated # characters make a right-justified numeric field. The following line supplies values in the same order as the fields.

Run it as an ordinary user:

$ perl /tmp/perl-report.pl
Name:  Ada Lovelace          Team: Research    Total:  42

Checkpoint: if the values are shifted into the wrong columns, count the fields from left to right and compare them with the comma-separated expressions. Picture lines do not interpolate variables themselves.

2. Add numeric precision and overflow checks

Use a dot inside a numeric field to reserve a decimal point. A leading zero in place of the first hash requests zero padding. If a value cannot fit, Perl prints hashes as visible overflow evidence rather than silently shortening the number.

format STDOUT =
Count: @###  Rate: @.###  Code: @0###  Limit: @###
        42,          3.1415,         7,       10000
.

write;

Expected output is:

Count:  42  Rate: 3.142  Code: 0007  Limit: ####

The field width is a limit, not a suggestion. Widen @### when a legitimate value can exceed three positions. An undefined value in a special caret field is blank, but an ordinary numeric field is not a substitute for input validation.

3. Wrap a long value without losing the remainder

For descriptions, a caret field consumes part of one scalar each time it is used. Perl removes the consumed text from that scalar during the write call. Put several caret fields in a vertical stack, and add ~~ to repeat a line until the field is exhausted:

my $description = 'Perl formats are useful for small reports when columns and page headers matter.';

format STDOUT =
Description: ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
             $description
~~           ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
             $description
.

write;

The output is split across lines at suitable break characters. The original scalar is deliberately altered, so do not expect to reuse $description after write unless you copied it first. A caret field is a formatting operation with state, not a read-only display expression.

To suppress a line whose caret fields have no text left, put one tilde on that line. It is rendered as a space. A pair of tildes means repeat the line, so check that at least one field will eventually become empty. A regular field fed by a value that never changes can create an endless report.

4. Choose between @* and ^*

Use @* when a scalar already contains multiple lines and you want all of them emitted without truncation. The final newline is removed, but other characters are emitted verbatim. Use ^* when you want one line of a scalar at a time and need the variable-width field behaviour:

my $text = "line 1\nline 2\nline 3";

format STDOUT =
Text: ^*
      $text
~~    ^*
      $text
.

write;

Expected output:

Text: line 1
      line 2
      line 3

Use ^* for a report field that should advance through input one line at a time. Use @* for a complete multi-line value. These fields are not interchangeable, and the caret form still consumes its scalar.

5. Add a page header only when you need pages

For a filehandle named STDOUT, Perl looks for a top-of-page format named STDOUT_TOP. It uses that format when a page starts. A header format can contain literal text and fields just like the body format:

format STDOUT_TOP =
                         Inventory report
Item                  Owner              Count
--------------------  -----------------  -----
.

format STDOUT =
@<<<<<<<<<<<<<<<<<<<<  @<<<<<<<<<<<<<<<<<  @###
$item,                 $owner,              $count
.

write;

Page behaviour depends on the selected filehandle's format variables, including the page length. Do not mix print and write casually: if you do, you are responsible for tracking the lines left before the next header. There is no automatic footer format. A fixed footer must be managed by your program using the format line-count variables.

6. Keep filehandle settings local and testable

The current format name, top format name, page number and lines per page are stored per filehandle. If you need a named format on another handle, select that handle, set its format names, then restore the previous handle:

my $old = select(REPORT);
$~ = 'Report';
$^ = 'Report_TOP';
select($old);

write(REPORT);

Use a temporary variable for the previous handle. The longer one-expression select idiom exists, but it is harder to inspect when debugging. Verify a named format by running the complete program and checking both headings and data columns. There is no undo command for output already written, so redirect tests to a temporary file if the report feeds another system.

Done means

  • perl -v reports the version you tested.
  • Your format has one value expression for each picture field, in order.
  • Text widths are large enough for real input, and numeric overflow is visible.
  • You know whether wrapped fields consume their source scalar.
  • A final run produces the expected headings, alignment and line breaks without elevated privileges.