Skip to content

Working With Strings


This section is about transforming data once it’s already in a variable — cleaning up a string, reshaping a list, merging two dictionaries, filling in a sensible default when something’s missing. We’re covering the general-purpose tools you’ll reach for constantly, across every data type Ansible works with.

We’re deliberately not covering the specialized filters for things like IP addresses, MAC addresses, Kubernetes resources, or UUID generation — those exist, they’re documented, and if you ever need one, the official filters guide and the complex data manipulation guide cover them well. This section is about the everyday tools, not the full catalog.

We start with strings.

Recap: Case Conversion

upper and lower already showed up back in the Jinja2 basics chapter:

msg: "{{ 'hello' | upper }}"
HELLO

Two more worth knowing: capitalize (uppercases just the first character, lowercases the rest) and title (uppercases the first letter of every word):

msg: "{{ 'hello world' | capitalize }}"
Hello world
msg: "{{ 'hello world' | title }}"
Hello World

Trimming Whitespace With trim

msg: "{{ '  hello  ' | trim }}"
hello

Leading and trailing whitespace gone — genuinely useful for values that came from somewhere less predictable than a value you typed yourself, like a registered command’s output.

Replacing Text With replace

msg: "{{ 'hello world' | replace('world', 'there') }}"
hello there

A plain, literal substitution — every occurrence of the first argument gets swapped for the second.

Pattern Matching With regex_replace And regex_search

For anything beyond a literal substring, regex_replace and regex_search bring real regular expressions into play:

msg: "{{ 'app-2026-08-23.log' | regex_replace('\\d{4}-\\d{2}-\\d{2}', 'DATE') }}"
app-DATE.log
msg: "{{ 'port: 8080' | regex_search('\\d+') }}"
8080

Note

regex_search returns nothing at all (None) if the pattern doesn’t match — not an error, just an empty result. Combined with default, covered later in this section, that’s exactly how you handle a pattern that might not always be present without the playbook failing outright.

Quoting Values Safely With quote

quote wraps a string in shell-safe quoting, escaping anything that would otherwise be interpreted as special by a shell:

msg: "{{ \"it's a test\" | quote }}"
'it'"'"'s a test'

This matters most when a variable’s value is heading into a command or shell task — exactly the same concern as safely quoting a variable in a shell script: an unquoted value containing spaces or special characters can silently change what actually gets executed. If you’re building a shell command string from a variable you don’t fully control, quote is the filter doing the job a careful shell script would do with its own quoting discipline.

Measuring Length With length

msg: "{{ 'hello' | length }}"
5

length isn’t string-specific — it works on lists and dictionaries too, both covered later in this section, and reports the count of elements or keys respectively.

Concatenation With ~

Jinja2 uses ~, not +, to join values into a string:

vars:
  name: "Alice"
  age: 30
tasks:
  - name: Greet
    ansible.builtin.debug:
      msg: "{{ 'Hello, ' ~ name ~ '! You are ' ~ age ~ ' years old.' }}"
Hello, Alice! You are 30 years old.

Notice age, a number, got joined in directly alongside plain strings — ~ converts everything to a string automatically before joining.

A Gotcha: + Doesn’t Do What You Might Expect

msg: "{{ 'Hello, ' + name }}"

If name is a genuine string, this works. But swap in a number, or anything that isn’t already a string, and + fails outright — it expects both sides to already be the same type, and won’t convert one for you the way ~ does. The practical rule: use ~ for building strings out of mixed values, and reserve + for numeric addition or actual list concatenation — covered in the next chapter, where + does something completely different and entirely correct.

Best Practices

  • Use ~ for string concatenation, not + — it converts non-string values automatically, and won’t fail the moment a number shows up in the mix.
  • Reach for quote any time a variable’s value is heading into a command or shell task, especially if that value didn’t come from something you typed yourself.
  • Pair regex_search with default (covered later in this section) whenever the pattern genuinely might not match — an empty result isn’t an error, but using it without a fallback can lead to one further downstream.
Last updated on