Ansible regex_search Filter: Search Strings with Regex Patterns (Guide)
By Luca Berton · Published 2024-01-01 · Category: troubleshooting
How to search strings with regex in Ansible using the regex_search filter. Extract matches, capture groups, validate patterns in Jinja2.
The regex_search filter in Ansible extracts text from strings using regular expressions. It's invaluable for parsing command output, extracting values from configuration files, and implementing conditional logic based on pattern matching. This guide covers syntax, capture groups, multiline matching, and real-world examples for production automation.
Understanding regex_search
regex_search searches a string for a regex pattern and returns the first match. If no match is found, it returns an empty string (which is falsy in Jinja2). This makes it perfect for both extraction and conditional logic.
Basic Syntax
# Returns the matched string or empty string
{{ my_string | regex_search('pattern') }}
# With flags (multiline, ignorecase, etc.)
{{ my_string | regex_search('pattern', multiline=True, ignorecase=True) }}
# With capture groups - returns the group
{{ my_string | regex_search('version: (\d+\.\d+)', '\\1') }}
See also: Ansible split Filter: Split Strings into Lists (Complete Guide)
Practical Examples
Example 1: Extract Version Numbers
---
- name: Parse version information
hosts: all
tasks:
- name: Get application version
ansible.builtin.command:
cmd: nginx -v
register: nginx_output
changed_when: false
failed_when: false
- name: Extract version number
ansible.builtin.set_fact:
nginx_version: "{{ nginx_output.stderr | regex_search('nginx/([\\d.]+)', '\\1') | first }}"
nginx_major: "{{ nginx_output.stderr | regex_search('nginx/(\\d+)\\.', '\\1') | first }}"
- name: Display version
ansible.builtin.debug:
msg: "Nginx version: {{ nginx_version }}, Major: {{ nginx_major }}"
Example 2: Parse Configuration Values
---
- name: Extract config values
hosts: all
tasks:
- name: Read config file
ansible.builtin.slurp:
src: /etc/myapp/config.conf
register: config_file
- name: Parse config values
ansible.builtin.set_fact:
config_content: "{{ config_file.content | b64decode }}"
- name: Extract specific settings
ansible.builtin.set_fact:
db_port: "{{ config_content | regex_search('db_port\\s*=\\s*(\\d+)', '\\1') | first | default('5432') }}"
db_host: "{{ config_content | regex_search('db_host\\s*=\\s*(\\S+)', '\\1') | first | default('localhost') }}"
max_connections: "{{ config_content | regex_search('max_connections\\s*=\\s*(\\d+)', '\\1') | first | default('100') }}"
- name: Display parsed config
ansible.builtin.debug:
msg: "DB: {{ db_host }}:{{ db_port }}, Max Connections: {{ max_connections }}"
Example 3: Conditional Logic with Pattern Matching
---
- name: Conditional actions based on patterns
hosts: all
tasks:
- name: Get kernel version
ansible.builtin.command:
cmd: uname -r
register: kernel_info
changed_when: false
- name: Check if running RHEL kernel
ansible.builtin.set_fact:
is_rhel_kernel: "{{ kernel_info.stdout | regex_search('\\.el[0-9]+') | length > 0 }}"
kernel_major: "{{ kernel_info.stdout | regex_search('^(\\d+)\\.', '\\1') | first }}"
- name: Apply RHEL-specific tuning
ansible.builtin.template:
src: sysctl-rhel.conf.j2
dest: /etc/sysctl.d/99-custom.conf
when: is_rhel_kernel | bool
Example 4: Parse Log Files for Errors
---
- name: Analyze log files
hosts: all
tasks:
- name: Read recent log entries
ansible.builtin.command:
cmd: tail -100 /var/log/myapp/error.log
register: log_content
changed_when: false
failed_when: false
- name: Check for critical errors
ansible.builtin.set_fact:
has_oom_error: "{{ log_content.stdout | regex_search('Out[Oo]f[Mm]emory|OOM|Cannot allocate memory') | default('', true) | length > 0 }}"
has_disk_error: "{{ log_content.stdout | regex_search('No space left on device|ENOSPC') | default('', true) | length > 0 }}"
last_error_time: "{{ log_content.stdout | regex_search('(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}).*ERROR', '\\1') | first | default('none') }}"
- name: Alert on critical errors
ansible.builtin.debug:
msg: "CRITICAL: OOM detected, last error at {{ last_error_time }}"
when: has_oom_error | bool
Example 5: Extract IP Addresses and Network Info
---
- name: Parse network information
hosts: all
tasks:
- name: Get IP configuration
ansible.builtin.command:
cmd: ip addr show eth0
register: ip_output
changed_when: false
- name: Extract network details
ansible.builtin.set_fact:
ipv4_address: "{{ ip_output.stdout | regex_search('inet (\\d+\\.\\d+\\.\\d+\\.\\d+)', '\\1') | first }}"
ipv4_cidr: "{{ ip_output.stdout | regex_search('inet \\d+\\.\\d+\\.\\d+\\.\\d+/(\\d+)', '\\1') | first }}"
mac_address: "{{ ip_output.stdout | regex_search('link/ether ([0-9a-f:]+)', '\\1') | first }}"
mtu: "{{ ip_output.stdout | regex_search('mtu (\\d+)', '\\1') | first }}"
- name: Display network info
ansible.builtin.debug:
msg: "IP: {{ ipv4_address }}/{{ ipv4_cidr }}, MAC: {{ mac_address }}, MTU: {{ mtu }}"
Example 6: Multiline Pattern Matching
---
- name: Multiline regex parsing
hosts: all
tasks:
- name: Read SSL certificate info
ansible.builtin.command:
cmd: openssl x509 -in /etc/ssl/server.crt -noout -text
register: cert_info
changed_when: false
- name: Extract certificate details
ansible.builtin.set_fact:
cert_cn: "{{ cert_info.stdout | regex_search('Subject:.*CN\\s*=\\s*([^\\n,]+)', '\\1', multiline=True) | first | trim }}"
cert_expiry: "{{ cert_info.stdout | regex_search('Not After\\s*:\\s*(.+)', '\\1', multiline=True) | first | trim }}"
cert_issuer: "{{ cert_info.stdout | regex_search('Issuer:.*CN\\s*=\\s*([^\\n,]+)', '\\1', multiline=True) | first | trim }}"
- name: Check certificate validity
ansible.builtin.debug:
msg: "Certificate for {{ cert_cn }} expires: {{ cert_expiry }}, issued by {{ cert_issuer }}"
regex_search vs regex_findall vs regex_replace
| Filter | Returns | Use Case |
|--------|---------|----------|
| regex_search | First match (string) | Extract a single value |
| regex_findall | All matches (list) | Find all occurrences |
| regex_replace | Modified string | Transform text |
# regex_search - get first match
version: "{{ output | regex_search('v(\\d+\\.\\d+)', '\\1') | first }}"
# regex_findall - get all matches
all_ips: "{{ output | regex_findall('\\d+\\.\\d+\\.\\d+\\.\\d+') }}"
# regex_replace - transform text
clean: "{{ output | regex_replace('\\s+', ' ') }}"
See also: Ansible Split String: Filter Guide for CSV, Delimiters & Lists
Regex Flags
| Flag | Description |
|------|-------------|
| ignorecase=True | Case-insensitive matching |
| multiline=True | ^ and $ match line boundaries |
# Case-insensitive search
{{ text | regex_search('error|warning|critical', ignorecase=True) }}
# Multiline (^ matches start of each line)
{{ text | regex_search('^ERROR:.*$', multiline=True) }}
Common Patterns Reference
# IP address
{{ text | regex_search('\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}') }}
# Email address
{{ text | regex_search('[\\w.+-]+@[\\w-]+\\.[\\w.]+') }}
# Semantic version
{{ text | regex_search('(\\d+\\.\\d+\\.\\d+)', '\\1') | first }}
# UUID
{{ text | regex_search('[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}') }}
# Key=value pair
{{ text | regex_search('mykey=(\\S+)', '\\1') | first }}
# Between delimiters
{{ text | regex_search('\\[(.+?)\\]', '\\1') | first }}
See also: Ansible set_fact Module: Set Variables Dynamically (Complete Guide 2026)
Troubleshooting
Empty String vs None
regex_search returns an empty string when no match is found. Always use default():
# Safe extraction with default
{{ output | regex_search('version: (\\S+)', '\\1') | first | default('unknown') }}
Escaping Backslashes
In YAML, backslashes need double-escaping in double-quoted strings:
# Double-quoted: escape twice
result: "{{ text | regex_search('\\d+\\.\\d+') }}"
# Single-quoted or block: no double escape needed in the regex itself
# but Jinja2 still needs \\
Capture Group with first
When using capture groups, regex_search returns a list. Always pipe through first:
# Returns a list ['1.0'] - need | first
{{ 'version: 1.0' | regex_search('version: (\\S+)', '\\1') | first }}
Frequently Asked Questions
What does regex_search return when there is no match in Ansible?
It returns an empty string "", which is falsy in Jinja2. You can use this for conditional logic: when: my_var | regex_search('pattern') will be false when there's no match.
How do I use capture groups with regex_search?
Pass the group reference as an additional argument: regex_search('pattern (group1) (group2)', '\\1'). This returns a list of matches for that group. Pipe through | first to get the string value.
What is the difference between regex_search and regex_findall?
regex_search returns only the first match as a string (or list with capture groups). regex_findall returns all matches as a list. Use regex_search when you need one value, regex_findall when you need all occurrences.
Can I use regex_search in a when condition?
Yes. Since an empty string is falsy, you can write: when: variable | regex_search('pattern'). This runs the task only if the pattern matches.
How do I make regex_search case-insensitive?
Add ignorecase=True: {{ text | regex_search('error', ignorecase=True) }}.
Related Articles
• Ansible Jinja2 Filters • Ansible set_fact Module • Ansible builtin command Module • Ansible Error HandlingConclusion
The regex_search filter is a powerful tool for extracting and validating data in Ansible playbooks. Combined with set_fact and register, it enables your automation to parse command output, validate configurations, and make decisions based on text patterns. Master the patterns in this guide — version extraction, config parsing, log analysis, and network info parsing — and you'll handle any text processing challenge in your Ansible workflows.
Category: troubleshooting