Reputation: 1432
Essentially, I want to do something like:
['hello', 'apple', 'rare', 'trim', 'three'] | select(match('.*a[rp].*'))
Which would yield:
['apple', 'rare']
The match
filter and select
filter. My issue arises from the fact that the select filter only supports unary "tests".
I'm on Ansible 1.9.x.
...is closer to:
lookup('dig', ip_address, qtype="PTR", wantList=True) | select(match("mx\\..*\\.example\\.com"))
So, I want to get all the PTR records associated with an IP and then filter out all the ones that don't fit a given regex. I'd also want to ensure that there's only one element in the resulting list, and output that element, but that's a different concern.
Upvotes: 14
Views: 63226
Reputation: 629
I found the following trick if you want to filter a list in Ansible (get the list with null values and make difference with null list) :
---
- hosts: localhost
connection: local
vars:
regexp: '.*a[rp].*'
empty: [null]
tasks:
- set_fact: matches="{{ ['hello', 'apple', 'rare', 'trim', 'three'] | map('regex_search',regexp) | list|difference(empty) }}"
- debug: msg="{{ matches }}"
Here is the output :
ok: [localhost] => {
"msg": [
"apple",
"rare"
]
}
Upvotes: 4
Reputation: 932
You can use select like this:
['hello', 'apple', 'rare', 'trim', 'three'] | select('match', '.*a[rp]')
Which would yield:
['apple', 'rare']
The match uses the re.match implementation and hence matches at beginning of string. Therefore you need .*
at beginning, but not at the end of the regexp.
Or you can use search
to avoid .*
altogether:
['hello', 'apple', 'rare', 'trim', 'three'] | select('search', 'a[rp]')
Upvotes: 5
Reputation: 68239
Will this do?
---
- hosts: localhost
connection: local
vars:
my_list: ['hello', 'apple', 'rare', 'trim', 'three']
my_pattern: '.*a[rp].*'
tasks:
- set_fact: matches="{{ my_list | map('regex_search',my_pattern) | select('string') | list }}"
failed_when: matches | count > 1
- debug: msg="I'm the only one - {{ matches[0] }}"
Update: how it works...
map applies filters – filters are not yes/no things, they applied to
every item of input list and return list of modified items. I use
regex_search
filter, which search pattern in every item and return
a match if found or None if there is no match. So on this step I get
this list: [null, "apple", "rare", null, null]
.
Then we use select, which applies tests – tests are yes/no things, so
they reduce the list based on selected test. I use string
test, which is
true when item of a list is string. So we get: ["apple", "rare"]
.
map
and select
give us some internal python types, so we convert to list
by applying list
filter after all.
Upvotes: 19
Reputation: 1432
This design pattern worked for me:
----
- hosts: localhost
connection: local
vars:
my_list: ['hello', 'apple', 'rare', 'trim', "apropos", 'three']
my_pattern: 'a[rp].*'
tasks:
- set_fact:
matches: "{%- set tmp = [] -%}
{%- for elem in my_list | map('match', my_pattern) | list -%}
{%- if elem -%}
{{ tmp.append(my_list[loop.index - 1]) }}
{%- endif -%}
{%- endfor -%}
{{ tmp }}"
- debug:
var: matches
failed_when: "(matches | length) > 1"
Upvotes: 3