How can I match `\s` or `\t` in a `search` test?

76 views
Skip to first unread message

tterr...@gmail.com

unread,
Jun 20, 2022, 5:36:37 AM6/20/22
to Ansible Project
Here is an example:

- hosts: localhost
  gather_facts: false
  vars:
    q:
      - 'abc<tab character here>'
      - 123
  tasks:
    - debug:
        msg: >-
          {{ q is search('abc\t') }}

This returns false. I have tried different variations of the regex, but nothing worked. Is this possible to do?

Dick Visser

unread,
Jun 20, 2022, 7:52:52 AM6/20/22
to ansible...@googlegroups.com
Escape the tab with another single quote, like this:


{{ q is search('abc\\t') }}



Dick
OpenPGP_0x266713D4E6EF488D.asc
OpenPGP_signature

Dick Visser

unread,
Jun 20, 2022, 7:53:53 AM6/20/22
to ansible...@googlegroups.com


On 2022-06-20 (Mon) 13:52, Dick Visser w

> Escape the tab with another single quote, like this:

I meant with another backslash obviously
OpenPGP_0x266713D4E6EF488D.asc
OpenPGP_signature

Vladimir Botka

unread,
Jun 20, 2022, 3:22:37 PM6/20/22
to tterr...@gmail.com, ansible...@googlegroups.com
On Mon, 20 Jun 2022 02:36:37 -0700 (PDT)
"tterr...@gmail.com" <tterr...@gmail.com> wrote:

> q:
> - 'abc<tab character here>'
> - 123
>
> - debug:
> msg: >-
> {{ q is search('abc\t') }}

There are more aspects to this question:

* It's necessary to understand the difference between 'Double-Quoted
Style' and 'Single-Quoted Style'. See
https://yaml.org/spec/1.2.2/#731-double-quoted-style
https://yaml.org/spec/1.2.2/#732-single-quoted-style

In 'Single-Quoted Style' only "'" has to be escaped. As a result, the
string 'abc\t' doesn't include TAB. It's a sequence of characters: a,
b, c, \, t. Test it. The task below

- copy:
dest: /tmp/test.txt
content: |
{{ ql.0 }}
{{ ql.1 }}
vars:
ql:
- 'abc\tx'
- 123

gives

shell> cat /tmp/test.txt
abc\tx
123

The string will include TAB if you use 'Double-Quoted Style'

- copy:
dest: /tmp/test.txt
content: |
{{ ql.0 }}
{{ ql.1 }}
vars:
ql:
- "abc\tx"
- 123

gives

shell> cat /tmp/test.txt
abc x
123

* The *search* test doesn't work with lists. It works with strings.
See
https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html#testing-strings

Use it to test a single item of a list. For example,

- debug:
msg: "{{ ql.0 is search(regex) }}"
vars:
regex: 'abc\tx'
ql:
- "abc\tx"
- 123

gives

msg: true

Also the task below, with single-quoted *item* and escaped backslash
in *regex*, gives the same result

- debug:
msg: "{{ ql.0 is search(regex) }}"
vars:
regex: 'abc\\tx'
ql:
- 'abc\tx'
- 123

* Instead, you can "Test if a list contains a value". See
https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html#testing-if-a-list-contains-a-value

For example, both tasks below returns *True*

- debug:
msg: "{{ item in ql }}"
vars:
item: 'abc\tx'
ql:
- 'abc\tx'
- 123

- debug:
msg: "{{ item in ql }}"
vars:
item: "abc\tx"
ql:
- "abc\tx"
- 123

* If you want to use *regex* to test an item in a list use *select*.
For example,

- debug:
msg: "{{ ql|select('search', regex) }}"
vars:
regex: 'abc\\tx'
ql:
- 'abc\tx'
- 123

gives

msg:
- abc\tx

, or use it in a condition. For example,

- debug:
msg: "Regex {{ regex }} is present in the list."
when: ql|select('search', regex)|length > 0
vars:
regex: 'abc\\tx'
ql:
- 'abc\tx'
- 123

gives

msg: Regex abc\\tx is present in the list.

--
Vladimir Botka

tterr...@gmail.com

unread,
Jun 20, 2022, 4:58:42 PM6/20/22
to Ansible Project
Thank you for your replies. The search test does work with lists and this is where the
issue lies. Your examples helped me clear this up. The search, regex, etc tests will
convert the object to a string. In python code, this means that the list will be turned
into a string with str(list_here). This will escape tab characters in the list items, so
that a list ["a", "b\t"] will get converted to the string, '["a", "b\\t"]'. To match this correctly
with the search test, I need to search for ...b\\t... in single quotes.

tterr...@gmail.com

unread,
Jun 20, 2022, 5:09:15 PM6/20/22
to Ansible Project
There is also the case that the search test appears in an assert:

- assert:
    that:
      - q is search('abc\\t')


The tab character needs to be double escaped here because (I think)
the assert clause is interpreted as a double quoted string.

tterr...@gmail.com

unread,
Jun 20, 2022, 5:10:35 PM6/20/22
to Ansible Project
Correction:

- assert:
    that:
      - q is search('abc\\t' | regex_escape)

using regex_escape to avoid escaping manually the backslashes ('abc\\\\t')

Vladimir Botka

unread,
Jun 20, 2022, 11:54:00 PM6/20/22
to ansible...@googlegroups.com, tterr...@gmail.com
On Mon, 20 Jun 2022 13:58:42 -0700 (PDT)
"tterr...@gmail.com" <tterr...@gmail.com> wrote:

> ... The search test does work with lists ...
> ... a list ["a", "b\t"] will get converted to the string, '["a", "b\\t"]'.
> ... I need to search for ...b\\t... in single quotes.

I see. You're right. It's worth to test the escaping. The simplest
case is when the items are double-quoted and *regex* is variable

- debug:
msg: "{{ _list is search(regex) }}"
vars:
_list: ["a", "b\t"]
regex: 'b\\t'

Additional escaping, because of the outside double-quotes, is needed
when *regex* is value

- debug:
msg: "{{ _list is search('b\\\\t') }}"
vars:
_list: ["a", "b\t"]

Additional escaping is also needed when the items are single-quoted
and *regex* is variable. This time because of '\\' is needed after
evaluation

- debug:
msg: "{{ _list is search(regex) }}"
vars:
_list: ['a', 'b\t']
regex: 'b\\\\t'

Finally, one more additional escaping is needed when you put the
*regex* as a value

- debug:
msg: "{{ _list is search('b\\\\\\\\t') }}"
vars:
_list: ['a', 'b\t']

All tasks above return True.

--
Vladimir Botka

Dick Visser

unread,
Jun 21, 2022, 12:41:18 AM6/21/22
to ansible...@googlegroups.com
This overview is worthy of its own paragraph in the ansible docs!
OpenPGP_0x266713D4E6EF488D.asc
OpenPGP_signature
Reply all
Reply to author
Forward
0 new messages