The switches#

Every one-liner is a combination of a program (passed to -e or -E) and a handful of switches that wrap implicit behaviour around it. This chapter covers the switches you will reach for. Each one earns its keep in the progression, regex, and numeric chapters that follow.

Readers coming from awk or sed already know what per-line iteration, field splitting, and in-place editing look like; the question is which pperl flag turns each on.

-e — run a program given on the command line#

pperl -e 'print "hello\n"'

-e takes the Perl source as its argument. Multiple -e chunks concatenate; they behave as one program with semicolons between them.

pperl -e 'my $x = 41;' -e 'print $x + 1, "\n"'    # 42

Use single quotes around the program so the shell leaves $_, $1, \n, and backticks alone. See gotchas for what goes wrong with double quotes.

-E-e plus the modern feature set#

-E is -e with all optional features enabled: say, state, fc, the // defined-or operator, unicode string semantics under use feature 'unicode_strings', and more.

pperl -E 'say "hello"'                    # instead of print "hello\n"
pperl -E 'say "pi = " . (atan2(1,1)*4)'

Reach for -E when writing new one-liners. Reach for -e only when copy-paste compatibility with very old scripts matters.

-n — wrap an implicit read loop around the program#

-n turns the program into the body of a loop over every line of every input file (or STDIN if none are given). The loop is exactly:

while (<>) {
    # your program
}

$_ holds each input line, including its trailing newline. Nothing is printed automatically — you decide what reaches STDOUT.

# Print lines containing "error"
pperl -ne 'print if /error/' app.log

Equivalent long form, shown once so the mechanical expansion is clear:

while (<>) {
    print if /error/;
}

Every subsequent -n recipe in this guide relies on that expansion — assume it without re-stating.

BEGIN { } runs before the loop, END { } runs after. Both are ordinary Perl blocks and compose with -n cleanly.

# Count lines matching a pattern
pperl -ne '$n++ if /WARN/; END { print "$n\n" }' app.log

-p — like -n, but print $_ after each iteration#

pperl -pe 's/foo/bar/g' input.txt

Mechanical expansion:

while (<>) {
    # your program
} continue {
    print or die "-p destination: $!";
}

Reach for -p when the output is the (possibly modified) input. Reach for -n when the output is a summary, a filtered subset, or nothing at all.

-l — line-end handling#

-l does two things at once:

  1. On input, chomp each line as it is read under -n / -p. $_ no longer ends in "\n".

  2. On output, set $\ (the output record separator) so every print appends "\n".

# Print just the first field of each tab-separated line
pperl -F'\t' -lane 'print $F[0]' table.tsv

Without -l, you would either write print "$F[0]\n" (the newline moves into the program) or produce glued output.

-l accepts an optional octal argument that sets $\ to a different character: -l012 keeps newline (the default), -l0 sets $\ to NUL, and so on. The common use is the no-argument form.

-a — autosplit into @F#

Combined with -n or -p, -a splits $_ on whitespace and puts the fields into @F. The default split is split(' ', $_) — it trims leading whitespace and treats any run of whitespace as one separator.

# Third column of every line
pperl -lane 'print $F[2]' data.txt

# Reverse column order
pperl -lane 'print "@{[reverse @F]}"' data.txt

@F is zero-indexed. $F[-1] is the last field. scalar @F is the field count.

-F — change the split pattern#

-F sets the delimiter used by -a. The argument is a regex; a literal comma is -F,, a colon is -F:, a tab is -F'\t'.

# Second field of a colon-separated file
pperl -F: -lane 'print $F[1]' /etc/passwd

# Split on any run of commas or semicolons
pperl -F'[,;]+' -lane 'print scalar @F' mixed.csv

-F implies nothing about -a; you still need -a. The common idiom -F: -lane is colon-split, chomped, with @F available.

Quoting a tab requires a literal tab in the shell or the regex form:

pperl -F'\t' -lane 'print $F[0]' data.tsv       # regex form, always safe
pperl -F'	' -lane 'print $F[0]' data.tsv     # literal tab — fragile

-i — edit files in place#

-i rewrites the file pperl is reading from, replacing its contents with whatever -p prints. The whole file’s output goes to a temporary file; when the loop finishes, that temporary replaces the original.

# Replace CRLF with LF in every .txt under cwd (no backup)
pperl -i -pe 's/\r\n/\n/g' *.txt

# Same, keeping a .bak copy beside each original
pperl -i.bak -pe 's/\r\n/\n/g' *.txt

Always use -i.bak (or another suffix) until you have verified the edit against a representative file. A bug in the program with bare -i destroys the input. See gotchas.

-i has no effect without -p (or explicit prints under -n): if nothing is printed, the replacement file is empty.

-M and -m — load modules before the program runs#

-Mmodule is equivalent to use module; at the top of the program. -Mmodule=sym1,sym2 is use module qw(sym1 sym2);.

pperl -MList::Util=sum -lane 'print sum @F' data.txt
pperl -MPOSIX=strftime -le 'print strftime("%F", localtime)'
pperl -MData::Dumper -e 'print Dumper({a=>1, b=>[2,3]})'

Lower-case -m is no strict rather than use ; it is rarely needed on the command line.

Modules most common in one-liners: List::Util (sum/min/max/uniq/any), POSIX (strftime, mktime, fmod), Data::Dumper (inspect structures), JSON::PP (parse/emit JSON), Text::CSV (quoted CSV), MIME::Base64, Digest::MD5, Digest::SHA, Socket.

-0 — set the input record separator#

Perl normally reads a line at a time. -0NNN sets $/ to the character with the given octal code, changing what “one record” means for <>, <STDIN>, and the -n / -p loop.

Common forms:

Form

What $/ becomes

Effect

-0

"\0" (NUL)

Read NUL-delimited records (matches find -print0).

-0777

undef

Slurp the whole file as one record.

-00

""

Paragraph mode: blank lines separate records.

-0NNN

chr(0NNN)

Any single octal byte.

# Count files from find -print0
find . -type f -print0 | pperl -0 -ne '$n++; END { print "$n\n" }'

# Slurp, then run a cross-line regex
pperl -0777 -ne 'print "yes\n" if /BEGIN.*?END/s' text.txt

# Print every paragraph containing "TODO"
pperl -00 -ne 'print if /TODO/' notes.txt

Paragraph mode (-00) interacts with -l: without -l, the blank separator stays attached to each record; with -l, it is chomped.

-C — Unicode on stdio and/or @ARGV#

-C enables Unicode handling on specified streams. The argument is either a number (bitmask) or a string of letters.

pperl -CSD -ne 'print' input.txt      # stdin, stdout, stderr all UTF-8
pperl -CA  -e  'print $ARGV[0]' äöü   # @ARGV treated as UTF-8
pperl -CSA -nE 'say length'           # S + A

Letter codes: I = stdin, O = stdout, E = stderr, S = I+O+E, A = @ARGV, D = set default I/O layers (includes file opens).

For a fully UTF-8 session, -CSD. For one-off processing of named UTF-8 files, -CSAD. See gotchas for the symptoms that tell you -C is missing.

Reference table#

Every switch, its mechanical expansion, and the chapter where it first shows up in a recipe.

Switch

Expands to

First used in

-e CODE

Run CODE as the program

progression

-E CODE

-e plus all current features enabled

progression

-n

while (<>) { CODE }

progression

-p

while (<>) { CODE } continue { print }

progression

-l

chomp input under -n/-p; set $\ = "\n" for output

progression

-a

Auto-split $_ into @F

progression

-F PAT

Change -a split pattern to regex PAT

progression

-i[EXT]

Edit in place; with EXT, keep backup original + EXT

progression

-M MOD

use MOD; before program

numeric

-M MOD=X

use MOD qw(X);

numeric

-0

$/ = "\0" — NUL-delimited records

progression

-00

$/ = "" — paragraph mode

progression

-0777

$/ = undef — slurp entire file

progression

-C

Enable Unicode on stdio, @ARGV, and/or default layers

gotchas

Special variables the switches set up#

Short catalogue; full definitions live in perlvar.

Variable

Role under -n/-p

$_

The current input line (chomped if -l, raw otherwise).

$.

Current input line number (not reset between files by default).

$/

Input record separator. -0 family changes it.

$\

Output record separator appended by print. -l sets it to "\n".

$,

Output field separator inserted between print’s arguments.

$"

Separator between array elements when interpolated in "@array".

@F

Auto-split fields under -a.

@ARGV

Remaining command-line arguments; filenames are consumed as processed.

$ARGV

Name of the file currently being read ("-" means STDIN).

Find out more#

  • progression applies every switch in recipes of increasing complexity.

  • perlvar lists every special variable pperl defines, not just the ones the switches touch.

  • perlop covers qr//, the flip-flop operator, and the diamond operator used by the -n / -p loops.