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

! ~ \ unary + unary -

6

left

=~ !~

7

left

* / % x

8

left

+ - .

9

left

<< >>

10

none

named unary operators

11

none

< <= > >= lt le gt ge

12

none

== != <=> eq ne cmp

13

left

&

14

left

| ^

15

left

&&

16

left

|| //

17

none

.. ...

18

right

?:

19

right

= += -= *= /= etc.

20

left

, =>

21

none

list operators (rightward)

22

right

not

23

left

and

24

left

or xor

A few rules of thumb:

  • Comparison and logic. Comparison (rows 11–12) is tighter than &&/||/// (rows 15–16), so $a < $b && $c > $d works without inner parens.

  • Bitwise vs comparison. &, |, ^ (rows 13–14) are looser than comparison. $x & 0xFF == 0 parses as $x & (0xFF == 0). Almost always parenthesise around bitwise.

  • Symbolic vs word logic. &&/||/! are tight (rows 5, 15, 16). and/or/not are 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 why my $fh = open ... or die works: or (row 24) is even looser than =, so the open finishes assigning before or decides.

  • List operators bookend the table. Row 1 looking leftward, row 21 looking rightward. A list operator like print or sort slurps everything to its right, up to the first operator that binds looser than row 21 — which is just not/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

-e

exists

boolean

-r

readable by effective UID

boolean

-w

writable by effective UID

boolean

-x

executable by effective UID

boolean

-o

owned by effective UID

boolean

-R

readable by real UID

boolean

-W

writable by real UID

boolean

-X

executable by real UID

boolean

-O

owned by real UID

boolean

-z

zero size

boolean

-s

non-zero size

size in bytes or false

-f

regular file

boolean

-d

directory

boolean

-l

symbolic link

boolean

-p

named pipe (FIFO)

boolean

-S

socket

boolean

-b

block special file

boolean

-c

character special file

boolean

-t

TTY (terminal)

boolean

-u

setuid bit

boolean

-g

setgid bit

boolean

-k

sticky bit

boolean

-T

text file (heuristic)

boolean

-B

binary file (heuristic)

boolean

-M

modification time (script-start age, days)

number

-A

access time (days)

number

-C

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.