Precedence#
Two pieces, in this order: the full precedence table, and the parse rule for named unary operators — built-in functions whose syntax binds at operator precedence rather than as full list-operator calls.
The full table#
Operators tighter at the top, looser at the bottom. Same row = same precedence; combining two same-row operators uses associativity. none = non-associative (combining two of them without parens is an error or a parse-time warning).
Row | Associativity | Operators |
|---|---|---|
1 | left | terms, list operators (leftward) |
2 | left |
|
3 | none |
|
4 | right |
|
5 | right |
|
6 | left |
|
7 | left |
|
8 | left |
|
9 | left |
|
10 | none | named unary operators |
11 | none |
|
12 | none |
|
13 | left |
|
14 | left |
|
15 | left |
|
16 | left |
|
17 | none |
|
18 | right |
|
19 | right |
|
20 | left |
|
21 | none | list operators (rightward) |
22 | right |
|
23 | left |
|
24 | left |
|
A few rules of thumb:
Comparison and logic. Comparison (rows 11–12) is tighter than
&&/||///(rows 15–16), so$a < $b && $c > $dworks without inner parens.Bitwise vs comparison.
&,|,^(rows 13–14) are looser than comparison.$x & 0xFF == 0parses as$x & (0xFF == 0). Almost always parenthesise around bitwise.Symbolic vs word logic.
&&/||/!are tight (rows 5, 15, 16).and/or/notare very loose (rows 22–24). Same logical meaning; different binding. The logical chapter shows where each is appropriate.=is loose. Row 19 — almost everything binds tighter than assignment. This is whymy $fh = open ... or dieworks:or(row 24) is even looser than=, so the open finishes assigning beforeordecides.List operators bookend the table. Row 1 looking leftward, row 21 looking rightward. A list operator like
printorsortslurps everything to its right, up to the first operator that binds looser than row 21 — which is justnot/and/or/xor. The named unary section below contrasts this with the row-10 single-argument case.
When in doubt, write parentheses. They cost nothing and document intent.
Post and pre increment / decrement#
Row 3 (++ and --) is non-associative. The pre and post forms differ in when the side-effect happens:
++$x— increment first, then yield the new value.$x++— yield the current value, then increment.
my $x = 5;
my $a = ++$x; # $x is now 6, $a is 6
my $b = $x++; # $a was 6, $x++ yields 6 then increments,
# so $b is 6 and $x is now 7
++ on a string performs Perl’s magic increment:
my $s = "aa";
$s++; # "ab"
$s++; # "ac"
# ...
$s = "az"; $s++; # "ba" — carries like base-26
$s = "Aa"; $s++; # "Ab" — case is preserved per-position
$s = "Az9"; $s++; # "Ba0" — digits and letters carry independently
The magic-increment is what makes 'a'..'zz' produce all 702 two-letter combinations in range.
Named unary operators#
Row 10 contains a category called named unary operators. These are not symbolic operators — they are built-in functions that take exactly one argument and parse with operator-style precedence (between bitshifts and comparison) rather than as full list operators.
The practical consequence is one parsing rule:
A named unary operator grabs its one argument greedily, and anything that would need more than one argument is not part of it.
defined $x + 1 # parses as: ( defined($x) ) + 1
# — NOT defined($x + 1)
length $s > 3 # parses as: ( length($s) ) > 3
-e $f && -r _ # both file tests are named-unary;
# `&&` combines two boolean results
Compare with a list operator (print, sort, push, …), which is at row 1 / 21 and slurps the rest of the expression:
print "n=", $n, "\n" # print's argument list is "n=", $n, "\n"
# all three commas belong to print
If you want a named-unary built-in to take a more complex argument, parenthesise:
defined($x + 1) # explicit: argument is the sum $x+1
length($s . "x") # length of $s with "x" appended
The unambiguous form is always available. When in doubt, parenthesise.
The list of named unary built-ins#
These built-ins parse at row-10 precedence. Each links to its perlfunc detail page — the parsing rule above is the only thing you need to know about them as operators; their function behaviour is in the perlfunc reference.
defined, exists, ref, scalar, length, uc, lc, ucfirst, lcfirst, chr, ord, hex, oct, int, abs, sqrt, sin, cos, log, exp, rand, srand, alarm, sleep, caller, wantarray, chroot, readlink, umask, lstat, stat (when given exactly one argument), plus all the file test operators (-e, -r, -w, -x, -f, -d, -l, -s, and the rest — see the file test operators section below).
The ”operator“ framing in classical Perl documentation is historical. In modern Perl terms, these are functions whose argument-parsing places them between bitshift and comparison on the precedence table. The naming is what it is; the behaviour is what matters.
File test operators#
File tests are unary operators (named-unary precedence) that inspect a property of a file or filehandle. They begin with a hyphen and a single letter. All return boolean except -s (file size in bytes), -M/-A/-C (age in days, fractional), and the read-time tests -T/-B (boolean but slightly slower because they read the file).
Op | Test | Returns |
|---|---|---|
| exists | boolean |
| readable by effective UID | boolean |
| writable by effective UID | boolean |
| executable by effective UID | boolean |
| owned by effective UID | boolean |
| readable by real UID | boolean |
| writable by real UID | boolean |
| executable by real UID | boolean |
| owned by real UID | boolean |
| zero size | boolean |
| non-zero size | size in bytes or false |
| regular file | boolean |
| directory | boolean |
| symbolic link | boolean |
| named pipe (FIFO) | boolean |
| socket | boolean |
| block special file | boolean |
| character special file | boolean |
| TTY (terminal) | boolean |
| setuid bit | boolean |
| setgid bit | boolean |
| sticky bit | boolean |
| text file (heuristic) | boolean |
| binary file (heuristic) | boolean |
| modification time (script-start age, days) | number |
| access time (days) | number |
| inode change time (days) | number |
if (-e $file) { ... } # exists
if (-f $path && -r $path) { ... } # regular file and readable
my $size = -s $file; # size in bytes
if (-d $path) { ... } # is a directory
Stacked file tests. -f -w -r $file is shorthand for (-f $file) && (-w $file) && (-r $file) — the chain re-uses the same target. The pseudo-handle _ (single underscore) re-uses the previous file test’s stat cache to avoid extra stat() syscalls:
if (-f $file && -r _) { ... } # one stat() call, two checks
The trailing _ is the standard idiom for any chain of file tests on the same path.
See also#
Every other page in this reference links into the rows of this table from its own Precedence section. The cross-reference is intentional — when you need to combine an operator with another, this table is the answer.
perlfunc index — every name in the named-unary list is documented there.