Expressions and operators

Expressions are statements that resolve to values. You can use expressions almost anywhere a value is required. Expressions can be compounded with other expressions, and the entire combined expression resolves to a single value.

In the Puppet language, nearly everything is an expression, including literal values, references to variables, resource declarations, function calls, and more. In other words, almost all statements in the language resolve to a value and can be used anywhere that value would be expected.

Most of this page is about expressions that are constructed with operators. Operators take input values and operate on them (for example, mathematically) to result in some other value. Other kinds of expressions (for example, function calls) are described in more detail on other pages.

Some expressions have side effects and are used in Puppet primarily for their side effects, rather than for their result value. For example:Your code won't usually do anything with the value these expressions produce, but sometimes the value is useful for things like forming relationships to resources whose names can't be predicted until run time.
Important: The following statements are not typical expressions. They don't resolve to usable values and can only be used in certain contexts:
Expressions can be used almost everywhere, including:
  • The operand of another expression.

  • The condition of an if statement.

  • The control expression of a case statement or selector statement.

  • The assignment value of a variable.

  • The argument or arguments of a function call.

  • The title of a resource.

  • An entry in an array

  • A key or value of a hash.

Expressions cannot be used:
  • Where a literal name of a class or defined type is expected (for example, in class or define statements).

  • As the name of a variable (the name of the variable must be a literal name).

  • Where a literal resource type or name of a resource type is expected (for example, in the type position of a resource declaration).

You can surround an expression by parentheses to control the order of evaluation in compound expressions (for example, 10+10/5 is 12, and (10+10)/5 is 4), or to make your code clearer.

For formal descriptions of expressions constructed with operators and other elements of the Puppet language, see the Puppet language specification.

Operator expressions

There are two kinds of operators:
  • Infix operators, also called binary operators, appear between two operands:
    • $a = 1

    • 5 < 9

    • $operatingsystem != 'RedHat'

  • Prefix operators, also called unary operators, appear immediately before a single operand:
    • *$interfaces

    • !$is_virtual

Operands in an expression can be any other expression — anything that resolves to a value of the expected data type is allowed. Each operator has its own rules, described in the sections below, for the data types of its operands.
When you create compound expressions by using other expressions as operands, use parentheses for clarity and readability:
(90 < 7) and ('RedHat' == 'RedHat') # resolves to false
(90 < 7) or ('RedHat' in ['Linux', 'RedHat']) # resolves to true

Order of operations

Compound expressions are evaluated in a standard order of operations. Expressions wrapped in parentheses are evaluated first, starting from the innermost expression:
# This example resolves to 30, not 23:
notice( (7+8)*2 )
For the sake of clarity, use parentheses in all but the simplest compound expressions.
The precedence of operators, from highest to lowest, is:
Precedence Operator
1 ! (unary: not)
2 - (unary: numeric negation)
3 * (unary: array splat)
4 in
5 =~ and !~ (regex or data type match or non-match)
6 *, /, % (multiplication, division, and modulo)
7 + and - (addition/array concatenation and subtraction/array deletion)
8 << and >> (left shift and right shift)
9 == and != (equal and not equal)
10 >=, <=, >, and < (greater or equal, less or equal, greater than, and less than)
11 and
12 or
13 = (assignment)

Comparison operators

Comparison operators take operands of several data types, and resolve to Boolean values.

Comparisons of numbers convert the operands to and from floating point and integer values, such that 1.0 == 1 is true. However, keep in mind that floating point values created by division are inexact, so mathematically equal values can be slightly unequal when turned into floating point values.

You can compare any two values with equals == or not equals !=, but only strings, numbers, and data types that require values to have a defined order can be compared with the less than or greater than operators.

Note: Comparisons of string values are case insensitive for characters in the US ASCII range. Characters outside this range are case sensitive.

Characters are compared based on their encoding. For characters in the US ASCII range, punctuation comes before digits, digits are in the order 0, 1, 2, ... 9, and letters are in alphabetical order. For characters outside US ASCII, ordering is defined by their UTF-8 character code, which might not always place them in alphabetical order for a given locale.

== (equality)

Resolves to true if the operands are equal. Accepts the following data types as operands:
  • Numbers: Tests simple equality.

  • Strings: Tests whether two strings are identical, ignoring case as described in the Note, above.

  • Arrays and hashes: Tests whether two arrays or hashes are identical.

  • Booleans: Tests whether two Booleans are the same value.

  • Data types: Tests whether two data types would match the exact same set of values.

Values are considered equal only if they have the same data type. Notably, this means that 1 == "1" is false, and "true" == true is false.

!= (non-equality)

Resolves to false if the operands are equal. So, $x != $y is the same as !($x == $y). It has the same behavior and restrictions, but opposite result, as equality ==, above.

< (less than)

Resolves to true if the left operand is smaller than the right operand. Accepts numbers, strings, and data types; both operands must be the same type. When acting on data types, a less-than comparison is true if the left operand is a subset of the right operand.

> (greater than)

Resolves to true if the left operand is larger than the right operand. Accepts numbers, strings, and data types; both operands must be the same type. When acting on data types, a greater-than comparison is true if the left operand is a superset of the right operand.

<= (less than or equal to)

Resolves to true if the left operand is smaller than or equal to the right operand. Accepts numbers, strings, and data types; both operands must be the same type. When acting on data types, a less-than-or-equal-to comparison is true if the left operand is the same as the right operand or is a subset of it.

>= (greater than or equal to)

Resolves to true if the left operand is larger than or equal to the right operand. Accepts numbers, strings, and data types; both operands must be the same type. When acting on data types, a greater-than-or-equal-to comparison is true if the left operand is the same as the right operand or is a superset of it.

=~ (regex or data type match)

Resolves to true if the left operand matches the right operand. Matching means different things, depending on what the right operand is.

This operator is non-transitive with regard to data types. The right operand must be one of:
  • A regular expression (regex), such as /^[<>=]{7}/.

  • A stringified regular expression — that is, a string that represents a regular expression, such as "^[<>=]{7}".

  • A data type, such as Integer[1,10].

If the right operand is a regular expression or a stringified regular expression, the left operand must be a string, and the expression resolves to true if the string matches the regular expression.

If the right operand is a data type, the left operand can be any value. The expression resolves to true if the left operand has the specified data type. For example, 5 =~ Integer and 5 =~ Integer[1,10] are both true.

!~ (regex or data type non-match)

Resolves to false if the left operand matches the right operand. So, $x !~ $y is the same as !($x =~ $y). It has the same behavior and restrictions, but opposite result, as regex match =~, above.

in

Resolves to true if the right operand contains the left operand. The exact definition of "contains" here depends on the data type of the right operand. See table below.

This operator is non-transitive with regard to data types. It accepts:
  • A string, regular expression, or data type as the left operand.

  • A string, array, or hash as the right operand.

Expression How in expression is evaluated
String in String Tests whether the left operand is a substring of the right, ignoring case:
'eat' in 'eaten' # resolves to true
'Eat' in 'eaten' # resolves to true
String in Array Tests whether one of the members of the array is identical to the left operand, ignoring case:
'eat' in ['eat', 'ate', 'eating'] # resolves to true
'Eat' in ['eat', 'ate', 'eating'] # resolves to true
String in Hash Tests whether the hash has a key identical to the left operand, ignoring case:
'eat' in { 'eat' => 'present tense', 'ate' => 'past tense'} # resolves to true
'eat' in { 'present' => 'eat', 'past' => 'ate' } # resolves to false
Regex in String Tests whether the right operand matches the regular expression:
# note the case-insensitive option ?i
/(?i:EAT)/ in 'eatery' # resolves to true
Regex in Array Tests whether one of the members of the array matches the regular expression:
/(?i:EAT)/ in ['eat', 'ate', 'eating'] # resolves to true
Regex in Hash Tests whether the hash has a key that matches the regular expression:
/(?i:EAT)/ in { 'eat' => 'present tense', 'ate' => 'past tense'} # resolves to true
/(?i:EAT)/ in { 'present' => 'eat', 'past' => 'ate' } # resolves to false
Data type in Array Tests whether one of the members of the array matches the data type:
# looking for integers between 100 and 199
Integer[100, 199] in [1, 2, 125] # resolves to true
Integer[100, 199] in [1, 2, 25]  # resolves to false
Data type in anything else Always false.

Boolean operators

Boolean expressions resolve to boolean values. They are most useful when creating compound expressions.

A boolean operator takes boolean operands. If you pass in another type, it will be converted to boolean; see the section Automatic conversion to boolean in the data type documentation.

and

Resolves to true if both operands are true, otherwise resolves to false.

or

Resolves to true if either operand is true.

! (not)

Takes one operand. Resolves to true if the operand is false, and false if the operand is true.
$my_value = true
notice ( !$my_value ) # Resolves to false

Arithmetic operators

Arithmetic expressions resolve to numeric values. Except for the unary negative -, arithmetic operators take two numeric operands. If an operand is a string, it's converted to numeric form. The operation fails if a string can't be converted.

+ (addition)

Resolves to the sum of the two operands.

- (subtraction and negation)

When used with two operands, resolves to the difference of the two operands, left minus right. When used with one operand, returns the value of subtracting that operand from zero.

/ (division)

Resolves to the quotient of the two operands, the left divided by the right.

* (multiplication)

Resolves to the product of the two operands. The asterisk is also used as a unary splat operator for arrays (see below).

% (modulo)

Resolves to the remainder of dividing the left operand by the right operand:
5 % 2   # resolves to 1

32 % 7  # resolves to 4

<< (left shift)

Left bitwise shift: shifts the left operand by the number of places specified by the right operand. This is equivalent to rounding both operands down to the nearest integer, and multiplying the left operand by 2 to the power of the right operand:
4 << 3   # resolves to 32: 4 times two cubed

>> (right shift)

Right bitwise shift: shifts the left operand by the number of places specified by the right operand. This is equivalent to rounding each operand down to the nearest integer, and dividing the left operand by 2 to the power of the right operand:
16 << 3   # resolves to 2: 16 divided by two cubed

Array operators

Array operators take arrays as operands, and, with the exception of * (unary splat), they resolve to array values.

* (splat)

The unary splat operator * accepts a single array value. If you pass it a scalar value, it converts the value to a single-element array first. The splat operator "unfolds" an array, resolving to a comma-separated list values representing the array elements. It's useful in places where a comma-separated list of values is valid, including:
  • The arguments of a function call.

  • The cases of a case statement.

  • The cases of a selector statement.

If you use it in other contexts, it resolves to the array that was passed in.
For example:
$a = ['vim', 'emacs']
myfunc($a)    # Calls myfunc with a single argument, the array containing 'vim' and 'emacs'
:
                   # myfunc(['vim','emacs'])
myfunc(*$a)   # Calls myfunc with two arguments, 'vim' and 'emacs': 
                   # myfunc('vim','emacs')
Another example:
$a = ['vim', 'emacs']
$x = 'vim'
notice case $x {
  $a      : { 'an array with both vim and emacs' }
  *$a     : { 'vim or emacs' }
  default : { 'no match' }
}

<< (append)

Resolves to an array containing the elements in the left operand, with the right operand as its final element.

The left operand must be an array, and the right operand can be any data type. Appending adds only a single element to an array. To add multiple elements from one array to another, use the concatenation operator +.

Examples:
[1, 2, 3] << 4 # resolves to [1, 2, 3, 4] [1, 2, 3] << [4, 5] # resolves to [1, 2, 3, [4, 5]]
The append operator does not change its operands; it creates a new value.

+ (concatenation)

Resolves to an array containing the elements in the left operand followed by the elements in the right operand.

Both operands must be arrays. If the left operand isn't an array, Puppet interprets + as arithmetic addition. If the right operand is a scalar value, it is converted to a single-element array first.

Hash values are converted to arrays instead of wrapped, so you must wrap them yourself.

Examples:
[1, 2, 3] + 1     # resolves to [1, 2, 3, 1]
[1, 2, 3] + [1]   # resolves to [1, 2, 3, 1]
[1, 2, 3] + [[1]] # resolves to [1, 2, 3, [1]]
The concatenation operator does not change its operands; it creates a new value.

- (removal)

Resolves to an array containing the elements in the left operand, with every occurrence of elements in the right operand removed.

Both operands must be arrays. If the left operand isn't an array, Puppet interprets - as arithmetic subtraction. If the right operand is a scalar value, it is converted to a single-element array first.

Hash values aren't automatically wrapped in arrays, so you must always wrap them yourself.

Examples:
[1, 2, 3, 4, 5, 1, 1] - 1    # resolves to [2, 3, 4, 5]
[1, 2, 3, 4, 5, 1, 1] - [1]  # resolves to [2, 3, 4, 5]
[1, 2, 3, [1, 2]] - [1, 2]   # resolves to [3, [1, 2]]
[1, 2, 3, [1, 2]] - [[1, 2]] # resolves to [1, 2, 3]
The removal operator does not change its operands; it creates a new value.

Hash operators

Hash operators accept hashes as their left operand, and hashes or specific kinds of arrays as their right operand. The expressions resolve to hash values.

+ (merging)

Resolves to a hash containing the keys and values in the left operand and the keys and values in the right operand. If a key is present in both operands, the final hash uses the value from the right. It does not merge hashes recursively; it only merges top-level keys.

The right operand can be one of the following:
  • A hash

  • An array with an even number of elements. Each pair is converted in order to a key-value hash pair.

Examples:
{a => 10, b => 20} + {b => 30}  # resolves to {a => 10, b => 30}
{a => 10, b => 20} + {c => 30}  # resolves to {a => 10, b => 30, c => 30}
{a => 10, b => 20} + [c, 30]    # resolves to {a => 10, b => 30, c => 30}
{a => 10, b => 20} + 30         # gives an error
{a => 10, b => 20} + [30]       # gives an error
The merging operator does not change its operands; it creates a new value.

- (removal)

Resolves to a hash containing the keys and values in the left operand, minus any keys that are also present in the right operand.

The right operand can be one of the following:
  • A hash. The keys present in this hash will be absent in the final hash, regardless of whether that key has the same values in both operands. The key, not the value, determines whether it's removed.

  • An array of keys.

  • A single key.

Examples:
{a => first, b => second, c => 17} - {c => 17, a => "something else"} # resolves to {b => second}
{a => first, b => second, c => 17} - {a => a, d => d}                 # resolves to {b => second, c => 17}
{a => first, b => second, c => 17} - [c, a]                           # resolves to {b => second}
{a => first, b => second, c => 17} - c                                # resolves to {a => first, b => second}
The removal operator does not change its operands; it creates a new value.

Assignment operator

Puppet has one assignment operator, =.

= (assignment)

The assignment operator sets the variable on the left side to the value on the right side. The expression resolves to the value of the right hand side. Variables can be set only one time, after which, attempts to set the variable to a new value cause an error.