# Extracting a substring with regex\_replace fails

**URL:** <https://forum.ansible.com/t/extracting-a-substring-with-regex-replace-fails/26730>\
**Category:** Ansible Project\
**Created:** [February 14, 2018, 3:10pm UTC](https://forum.ansible.com/t/extracting-a-substring-with-regex-replace-fails/26730 "2018-02-14T15:10:35Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![actionmystique](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/actionmystique/32/1678_2.png) [@actionmystique](https://forum.ansible.com/u/actionmystique)\
**Post date:** [February 14, 2018, 3:10pm UTC](https://forum.ansible.com/t/extracting-a-substring-with-regex-replace-fails/26730/1 "2018-02-14T15:10:35Z")

</div>

ansible 2.6.0 (devel da5cf72236) last updated 2018/02/14 14:29:49 (GMT +200)

The task seems obvious at first glance but it appears to be difficult to implement in this context.  
The string is:  
host\_meta: “\n \n”

I need to extract the values of rel and href into api\_release and api\_root.  
I unsuccessfully tried the following:

- set\_fact: api\_release=“{{ host\_meta | regex\_replace(‘^.\* rel=(._) ._$’, ‘\1’) }}”  
when: host\_meta is defined

- set\_fact: api\_root=“{{ host\_meta | regex\_replace(‘^.\* href=(._)/\>._$’, ‘\1’) }}”  
when: host\_meta is defined

Both variables contain the whole string instead of the corresponding substring, which should be:  
api\_release: ‘restconf’

api\_root: /restconf

I have already successfully used this filter in other contexts.  
What am I missing here? Is the filter confused by the string?

---

<div class="post-metadata">

**Author:** ![actionmystique](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/actionmystique/32/1678_2.png) [@actionmystique](https://forum.ansible.com/u/actionmystique)\
**Post date:** [February 14, 2018, 3:13pm UTC](https://forum.ansible.com/t/extracting-a-substring-with-regex-replace-fails/26730/2 "2018-02-14T15:13:05Z")

</div>

> 

I have the same difficulty with:

ansible 2.4.3.0 (detached HEAD 8a7f9beab7) last updated 2018/02/14 16:11:16 (GMT +200)

---

<div class="post-metadata">

**Author:** ![felixfontein](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/felixfontein/32/7530_2.png) [@felixfontein](https://forum.ansible.com/u/felixfontein)\
**Post date:** [February 14, 2018, 3:36pm UTC](https://forum.ansible.com/t/extracting-a-substring-with-regex-replace-fails/26730/3 "2018-02-14T15:36:33Z")

</div>

Hi,

> The task seems obvious at first glance but it appears to be difficult  
> to implement in this context.  
> The string is:  
> host\_meta: "\<XRD  
> xmlns='[http://docs.oasis-open.org/ns/xri/xrd-1.0](http://docs.oasis-open.org/ns/xri/xrd-1.0)’\>\n \<Link  
> rel='restconf' href='/restconf'/\>\n\</XRD\>"
> 
> I need to extract the values of rel and href into api\_release and  
> api\_root. I unsuccessfully tried the following:  
> - set\_fact: api\_release="{{ host\_meta | regex\_replace('^.\*  
> rel=(.\*) .\*$', '\\1') }}"  
> &nbsp;&nbsp;when: host\_meta is defined
> 
> - set\_fact: api\_root="{{ host\_meta | regex\_replace('^.\*  
> href=(.\*)/\>.\*$', '\\1') }}"  
> &nbsp;&nbsp;when: host\_meta is defined
> 
> Both variables contain the whole string instead of the corresponding  
> substring, which should be:  
> api\_release: 'restconf'  
> api\_root: /restconf
> 
> I have already successfully used this filter in other contexts.  
> What am I missing here? Is the filter confused by the string?

You are missing the newlines the string contains. regex\_replace uses  
re.sub() in Python and does not offer a way to set the MULTILINE flag.  
If you remove '^' and '$' from your regexps, you can see what happens:  
only the matching part between two '\n's is replaced.

Instead of regexp\_replace() you should use the regex\_search() filter;  
that works as expected:

&nbsp;&nbsp;- set\_fact: api\_release="{{ host\_meta | regex\_search('rel=(.\*)') }}"  
&nbsp;&nbsp;&nbsp;&nbsp;when: host\_meta is defined  
&nbsp;&nbsp;- set\_fact: api\_root="{{ host\_meta | regex\_search('href=(.\*)/\>') }}"  
&nbsp;&nbsp;&nbsp;&nbsp;when: host\_meta is defined

Yields:

&nbsp;&nbsp;"api\_release": "rel='restconf' href='/restconf'/\>"  
&nbsp;&nbsp;"api\_root": "href='/restconf'/\>"

(That's not exactly equal to what you expected, but that is what your  
original regexes would have returned if the multiline flag would have  
been set. The problem is that regexes are notoriously bad for matching  
XML and or HTML.)

Cheers,  
Felix

---

<div class="post-metadata">

**Author:** ![felixfontein](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/felixfontein/32/7530_2.png) [@felixfontein](https://forum.ansible.com/u/felixfontein)\
**Post date:** [February 14, 2018, 3:38pm UTC](https://forum.ansible.com/t/extracting-a-substring-with-regex-replace-fails/26730/4 "2018-02-14T15:38:23Z")

</div>

Hi,

instead of using regexes, you might want to use the XML module  
([https://docs.ansible.com/ansible/2.4/xml\_module.html](https://docs.ansible.com/ansible/2.4/xml_module.html)) with the  
xmlstring argument. That should allow you to do this in a much cleaner  
way.

Cheers,  
Felix

---

<div class="post-metadata">

**Author:** ![Kai\_Stian\_Olstad](https://avatars.discourse-cdn.com/v4/letter/k/e19adc/32.png) [@Kai\_Stian\_Olstad](https://forum.ansible.com/u/Kai_Stian_Olstad)\
**Post date:** [February 14, 2018, 5:59pm UTC](https://forum.ansible.com/t/extracting-a-substring-with-regex-replace-fails/26730/5 "2018-02-14T17:59:39Z")

</div>

It's possible to turn on multiline, but the dot doesn't match newline so DOTALL flag need to be set to.

This can be done with (?mS), "m" is multiline and "S" is DOTALL.

So this should work:

- set\_fact:  
&nbsp;&nbsp;&nbsp;&nbsp;api\_release: "{{ host\_meta | regex\_replace('(?ms)^.\* rel=(.\*) .\*$', '\\1') }}"

---

<div class="post-metadata">

**Author:** ![actionmystique](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/actionmystique/32/1678_2.png) [@actionmystique](https://forum.ansible.com/u/actionmystique)\
**Post date:** [February 15, 2018, 1:48pm UTC](https://forum.ansible.com/t/extracting-a-substring-with-regex-replace-fails/26730/6 "2018-02-15T13:48:51Z")

</div>

@Kai Stian Olstad  
Thanks for your answer: yes, it works.

@Felix Fontain  
I have no XML skills. I nevertheless took a look at the ansible xml module, and it’s obscure to me.  
I think you’re right though: it’d be better to read XML appropriately with that module rather than use some regex filter which can be easily defeated if the input changes in the future.  
So I tried something, but of course it does not work (api\_root\_filename is the file containing the xml multi-line string):

- name: Reading RESTconf release  
xml:  
attribute: rel  
content: attribute  
path: “{{ api\_root\_filename }}”  
xpath: /XRD/Link  
register: return\_restconf\_release

- name: Showing attribute value  
debug:  
var: return\_restconf\_release.matches[0].Link.rel

---

<div class="post-metadata">

**Author:** ![felixfontein](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/felixfontein/32/7530_2.png) [@felixfontein](https://forum.ansible.com/u/felixfontein)\
**Post date:** [February 15, 2018, 2:43pm UTC](https://forum.ansible.com/t/extracting-a-substring-with-regex-replace-fails/26730/7 "2018-02-15T14:43:50Z")

</div>

Hi,

> I have no XML skills. I nevertheless took a look at the ansible xml  
> module, and it's obscure to me.  
> I think you're right though: it'd be better to read XML appropriately  
> with that module rather than use some regex filter which can be  
> easily defeated if the input changes in the future.  
> So I tried something, but of course it does not work  
> (api\_root\_filename is the file containing the xml multi-line string):
> 
> - name: Reading RESTconf release  
> &nbsp;&nbsp;xml:  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;attribute: rel  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;content: attribute  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;path: "{{ api\_root\_filename }}"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xpath: /XRD/Link  
> &nbsp;&nbsp;register: return\_restconf\_release
> 
> - name: Showing attribute value  
> &nbsp;&nbsp;debug:  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var: return\_restconf\_release.matches[0].Link.rel

The problem here is that your XML uses a namespace. I'm not very  
familiar with xpaths either, but after a little googling I came up with  
this:

&nbsp;&nbsp;- name: Reading RESTconf release  
&nbsp;&nbsp;&nbsp;&nbsp;xml:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;attribute: rel  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;content: attribute  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;path: "{{ api\_root\_filename }}"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xpath: "/\*[name()='XRD']/\*[name()='Link']"  
&nbsp;&nbsp;&nbsp;&nbsp;register: return\_restconf\_release  
&nbsp;&nbsp;- name: Showing attribute value  
&nbsp;&nbsp;&nbsp;&nbsp;debug:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var: return\_restconf\_release.matches[0]['{[http://docs.oasis-open.org/ns/xri/xrd-1.0\](http://docs.oasis-open.org/ns/xri/xrd-1.0%5C)}Link']

This gives for me:

ok: [localhost] =\> {  
&nbsp;&nbsp;&nbsp;&nbsp;"failed": false,  
&nbsp;&nbsp;&nbsp;&nbsp;"return\_restconf\_release.matches[0]['{[http://docs.oasis-open.org/ns/xri/xrd-1.0\](http://docs.oasis-open.org/ns/xri/xrd-1.0%5C)}Link']": {  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"href": "/restconf",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"rel": "restconf"  
&nbsp;&nbsp;&nbsp;&nbsp;}  
}

Cheers,  
Felix

---

<div class="post-metadata">

**Author:** ![actionmystique](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/actionmystique/32/1678_2.png) [@actionmystique](https://forum.ansible.com/u/actionmystique)\
**Post date:** [February 16, 2018, 10:40am UTC](https://forum.ansible.com/t/extracting-a-substring-with-regex-replace-fails/26730/8 "2018-02-16T10:40:45Z")

</div>

You made it, bravo!  
However, this solution is not better than the regex search, because the whole point of using the xml module was to make it resilient to change. As soon as the URI {[http://docs.oasis-open.org/ns/xri/xrd-1.0](http://www.google.com/url?q=http%3A%2F%2Fdocs.oasis-open.org%2Fns%2Fxri%2Fxrd-1.0%257DLink&sa=D&sntz=1&usg=AFQjCNHIaLCF_P0TvmZvO7fZnRpnY4uu_g)} changes, the call to access both variables will fail.
