expressions

computed values (!expr)

An expression computes a value for each host, such as a list, a number, or true or false.

Syntax

The !expr tag marks an expression in the language of expr (expr-lang.org). Its result keeps its type: a list, a number, a boolean, a map, or a string.

Data

An expression reads these names without a leading dot:

varsthe variables of the host, with the vars of the recipe step
factsthe facts of the host; run winch doc facts
hostname, short, address, groups, and vars of the host; address is the name when the host has no address
itemthe current element in a step with loop
match(p)the hosts that match the host pattern p, with the fields of host

match(p) reads the inventory, not the --hosts selection. A host from match() has only the vars that need no facts. A default cannot use match().

Where expressions go

Any module argument can be an expression or a template, and so can then. when takes an expression. loop takes a list or an expression.

Operators

+ - * / % **arithmetic
== != < > <= >=comparison
and or notlogic, also && || !
inmembership in a list or a map
contains startsWith endsWithtext tests
matchesregular expression test
c ? a : bchoice
a ?? ba, or b when a is nil
[] ?.read an element or a key
x[i:j] 1..3slice and range
|pipe: vars.name | lower()

Predicates

In a predicate, such as the second argument of filter, # is the element, and .name reads the key name of a map element: filter(vars.users, .uid >= 1000).

Missing values

A missing map key is nil. A module argument whose expression gives null fails the host.

Quoting

Quote a YAML value that starts with a quote, [, {, &, *, or !, or that contains ": " or " #".

When they run

winch evaluates expressions on the machine that runs winch, during the plan. They cannot read the result of a step. winch checks each one when it loads the project: an unknown name or a syntax error is an error. An error while winch evaluates one for a host fails that host. The other hosts go on. $env and now() are errors in every expression.

Functions

FunctionMeaning
absabs(n): the absolute value of the number n.
allall(list, pred): true when pred is true for each element.
anyany(list, pred): true when pred is true for at least one element.
bitandbitand(a, b): the bitwise AND of two integers.
bitnandbitnand(a, b): the bitwise AND NOT of two integers.
bitnotbitnot(a): the bitwise NOT of an integer.
bitorbitor(a, b): the bitwise OR of two integers.
bitshlbitshl(a, n): a shifted left by n bits.
bitshrbitshr(a, n): a shifted right by n bits, with its sign.
bitushrbitushr(a, n): a shifted right by n bits, without its sign.
bitxorbitxor(a, b): the bitwise XOR of two integers.
ceilceil(n): n rounded up to an integer value.
concatconcat(a, b, ...): the lists joined into one list.
countcount(list, pred): the number of elements for which pred is true.
datedate(s), date(s, layout), or date(s, layout, zone): the time in the text s, such as date("2026-10-30").
durationduration(s): the duration in the text s, such as duration("1h30m").
filterfilter(list, pred): the elements for which pred is true: filter(match("all"), .name != host.name).
findfind(list, pred): the first element for which pred is true, or nil.
findIndexfindIndex(list, pred): the index of the first element for which pred is true, or -1.
findLastfindLast(list, pred): the last element for which pred is true, or nil.
findLastIndexfindLastIndex(list, pred): the index of the last element for which pred is true, or -1.
firstfirst(list): the first element, or nil for an empty list.
flattenflatten(list): the elements of the list and of its nested lists, in one list.
floatfloat(v): the number or the text v as a float.
floorfloor(n): n rounded down to an integer value.
fromBase64fromBase64(s): the text that the base64 text s encodes.
fromJSONfromJSON(s): the value of the JSON text s.
fromPairsfromPairs(list): a map from a list of [key, value] pairs.
getget(m, key): the value of key in the map m, or nil when m has no such key.
groupBygroupBy(list, pred): a map from each result of pred to the list of elements that give it.
hasPrefixhasPrefix(s, prefix): true when s starts with prefix.
hasSuffixhasSuffix(s, suffix): true when s ends with suffix.
indexOfindexOf(s, sub): the index of the first sub in s, or -1.
intint(v): the number or the text v as an integer: int("8080").
joinjoin(list, sep): the strings of list joined with sep.
jsonjson(v): v as JSON on one line.
keyskeys(m): the keys of the map m, in sorted order.
lastlast(list): the last element, or nil for an empty list.
lastIndexOflastIndexOf(s, sub): the index of the last sub in s, or -1.
lenlen(x): the length of a string, a list, or a map.
lowerlower(s): s in lower case.
mapmap(list, pred): the result of pred for each element: map(vars.users, .name).
matchmatch(pattern): the hosts that match the host pattern, each with name, short, address, groups, and vars, as host has.
maxmax(a, b, ...), or max(list): the largest number.
meanmean(list): the average of the numbers.
medianmedian(list): the median of the numbers.
minmin(a, b, ...), or min(list): the smallest number.
nonenone(list, pred): true when pred is false for each element.
oneone(list, pred): true when pred is true for exactly one element.
quotequote(s): s in single quotes for sh, each ' as '\''.
reducereduce(list, pred, initial): one value from all elements.
repeatrepeat(s, n): s n times.
replacereplace(s, old, new): s with each old replaced by new.
reversereverse(list): the elements in reverse order.
roundround(n): n rounded to the nearest integer value.
sortsort(list), or sort(list, "desc"): the elements in ascending or descending order.
sortBysortBy(list, pred), or sortBy(list, pred, "desc"): the elements in the order of the results of pred: sortBy(vars.users, .uid).
splitsplit(s, sep), or split(s, sep, n): the parts of s between each sep.
splitAftersplitAfter(s, sep), or splitAfter(s, sep, n): the parts of s after each sep, each with sep at its end.
sqlquotesqlquote(s): s as a string literal for MariaDB, or for PostgreSQL with E before it: in single quotes, each ' as '', each \ as \\.
stringstring(v): v as text: string(8080) is "8080".
sumsum(list), or sum(list, pred): the sum of the numbers, or of the results of pred.
taketake(list, n): the first n elements.
timezonetimezone(name): the time zone name, such as "Europe/Berlin", for the In method of a date.
toBase64toBase64(s): s in base64.
toPairstoPairs(m): the [key, value] pairs of the map m, in sorted key order.
trimtrim(s), or trim(s, chars): s without white space, or without the characters in chars, at both ends.
trimPrefixtrimPrefix(s, prefix): s without prefix at its start.
trimSuffixtrimSuffix(s, suffix): s without suffix at its end.
typetype(v): the type of v as text: nil, bool, int, float, string, array, map, time.Time for a date, or time.Duration.
uniquniq(list): the elements without repeats, in their first order.
upperupper(s): s in upper case.
valuesvalues(m): the values of the map m, in the order of its sorted keys.

expressions.abs

abs(n): the absolute value of the number n.

expressions.all

all(list, pred): true when pred is true for each element. In pred, # is the element, and .name reads the key name of a map element: all(vars.ports, # > 0).

expressions.any

any(list, pred): true when pred is true for at least one element.

expressions.bitand

bitand(a, b): the bitwise AND of two integers.

expressions.bitnand

bitnand(a, b): the bitwise AND NOT of two integers.

expressions.bitnot

bitnot(a): the bitwise NOT of an integer.

expressions.bitor

bitor(a, b): the bitwise OR of two integers.

expressions.bitshl

bitshl(a, n): a shifted left by n bits.

expressions.bitshr

bitshr(a, n): a shifted right by n bits, with its sign.

expressions.bitushr

bitushr(a, n): a shifted right by n bits, without its sign.

expressions.bitxor

bitxor(a, b): the bitwise XOR of two integers.

expressions.ceil

ceil(n): n rounded up to an integer value.

expressions.concat

concat(a, b, ...): the lists joined into one list.

expressions.count

count(list, pred): the number of elements for which pred is true. Without pred, the number of true elements.

expressions.date

date(s), date(s, layout), or date(s, layout, zone): the time in the text s, such as date("2026-10-30"). The layout is a Go time layout. now() is a load error, so a plan does not depend on the clock.

expressions.duration

duration(s): the duration in the text s, such as duration("1h30m").

expressions.filter

filter(list, pred): the elements for which pred is true: filter(match("all"), .name != host.name).

expressions.find

find(list, pred): the first element for which pred is true, or nil.

expressions.findIndex

findIndex(list, pred): the index of the first element for which pred is true, or -1.

expressions.findLast

findLast(list, pred): the last element for which pred is true, or nil.

expressions.findLastIndex

findLastIndex(list, pred): the index of the last element for which pred is true, or -1.

expressions.first

first(list): the first element, or nil for an empty list.

expressions.flatten

flatten(list): the elements of the list and of its nested lists, in one list.

expressions.float

float(v): the number or the text v as a float.

expressions.floor

floor(n): n rounded down to an integer value.

expressions.fromBase64

fromBase64(s): the text that the base64 text s encodes.

expressions.fromJSON

fromJSON(s): the value of the JSON text s.

expressions.fromPairs

fromPairs(list): a map from a list of [key, value] pairs.

expressions.get

get(m, key): the value of key in the map m, or nil when m has no such key. get(list, i) gives the element at the index i, or nil.

expressions.groupBy

groupBy(list, pred): a map from each result of pred to the list of elements that give it.

expressions.hasPrefix

hasPrefix(s, prefix): true when s starts with prefix. The operator startsWith does the same.

expressions.hasSuffix

hasSuffix(s, suffix): true when s ends with suffix. The operator endsWith does the same.

expressions.indexOf

indexOf(s, sub): the index of the first sub in s, or -1.

expressions.int

int(v): the number or the text v as an integer: int("8080").

expressions.join

join(list, sep): the strings of list joined with sep.

expressions.json

json(v): v as JSON on one line. Map keys are in sorted order, and non-ASCII characters stay UTF-8. The output is also a valid YAML value.

expressions.keys

keys(m): the keys of the map m, in sorted order. It cannot read vars itself: name each variable.

expressions.last

last(list): the last element, or nil for an empty list.

expressions.lastIndexOf

lastIndexOf(s, sub): the index of the last sub in s, or -1.

expressions.len

len(x): the length of a string, a list, or a map.

expressions.lower

lower(s): s in lower case.

expressions.map

map(list, pred): the result of pred for each element: map(vars.users, .name).

expressions.match

match(pattern): the hosts that match the host pattern, each with name, short, address, groups, and vars, as host has. It reads the inventory, not the --hosts selection. A host from match() has only the vars that need no facts. It works in steps, not in defaults. A constant pattern that matches no host is a load error.

expressions.max

max(a, b, ...), or max(list): the largest number.

expressions.mean

mean(list): the average of the numbers.

expressions.median

median(list): the median of the numbers.

expressions.min

min(a, b, ...), or min(list): the smallest number.

expressions.none

none(list, pred): true when pred is false for each element.

expressions.one

one(list, pred): true when pred is true for exactly one element.

expressions.quote

quote(s): s in single quotes for sh, each ' as '\''. A value goes into a command as it is. Quote a value that must stay one word: then: !expr '"postmap " + quote(vars.map)'.

expressions.reduce

reduce(list, pred, initial): one value from all elements. In pred, #acc is the value so far, # the element, and #index its index: reduce(vars.sizes, #acc + #, 0).

expressions.repeat

repeat(s, n): s n times.

expressions.replace

replace(s, old, new): s with each old replaced by new.

expressions.reverse

reverse(list): the elements in reverse order.

expressions.round

round(n): n rounded to the nearest integer value.

expressions.sort

sort(list), or sort(list, "desc"): the elements in ascending or descending order.

expressions.sortBy

sortBy(list, pred), or sortBy(list, pred, "desc"): the elements in the order of the results of pred: sortBy(vars.users, .uid).

expressions.split

split(s, sep), or split(s, sep, n): the parts of s between each sep. With n, at most n parts.

expressions.splitAfter

splitAfter(s, sep), or splitAfter(s, sep, n): the parts of s after each sep, each with sep at its end.

expressions.sqlquote

sqlquote(s): s as a string literal for MariaDB, or for PostgreSQL with E before it: in single quotes, each ' as '', each \ as \\. It assumes the default SQL mode, without NO_BACKSLASH_ESCAPES, and a UTF-8 client.

expressions.string

string(v): v as text: string(8080) is "8080".

expressions.sum

sum(list), or sum(list, pred): the sum of the numbers, or of the results of pred.

expressions.take

take(list, n): the first n elements.

expressions.timezone

timezone(name): the time zone name, such as "Europe/Berlin", for the In method of a date.

expressions.toBase64

toBase64(s): s in base64.

expressions.toPairs

toPairs(m): the [key, value] pairs of the map m, in sorted key order.

expressions.trim

trim(s), or trim(s, chars): s without white space, or without the characters in chars, at both ends.

expressions.trimPrefix

trimPrefix(s, prefix): s without prefix at its start.

expressions.trimSuffix

trimSuffix(s, suffix): s without suffix at its end.

expressions.type

type(v): the type of v as text: nil, bool, int, float, string, array, map, time.Time for a date, or time.Duration.

expressions.uniq

uniq(list): the elements without repeats, in their first order.

expressions.upper

upper(s): s in upper case.

expressions.values

values(m): the values of the map m, in the order of its sorted keys.

Example

# winch.yaml
name: demo
hosts:
  mail.acme.com: {}
  web1.acme.com: {}
vars:
  mail_server: false
  domain: acme.com
  admins: [ann, bob]
  mailname: !expr 'vars.mail_server ? vars.domain : host.name'
steps:
  - name: Set the mail name
    file:
      path: /etc/mailname
      content: !expr 'vars.mailname + "\n"'
    when: !expr vars.mail_server
  - name: Add the admins
    user:
      name: !expr item
      groups: [sudo]
    loop: !expr sort(vars.admins)
  - name: List the other hosts
    file:
      path: /etc/peers
      content: !expr 'join(map(filter(match("all"), .name != host.name), .address), "\n") + "\n"'