Skip to content
Advertisement

Disable metacharacters in regular expressions

Perl has Q and E operators in its regular expression toolkit:

"Foo (bar)" =~ /Foo Q(bar)E/; # Yup
"Foo (bar)" =~ /Fo. Q(bar)E/; # Yup
"Foo (bar)" =~ /Fo. Q(ba.)E/; # Nope

Does such a facility exist in Python? I am aware of the in operator to do literal string comparison, but my usecase is that I’m using the lineinfile Ansible module which relies on Python’s regular expression library. I’d like something like this (if you can see the intent even though this doesn’t work):

- lineinfile:
    path: /somewhere/over/the/rainbow
    regexp: "^Q{{ item }}E$"
    line: "{{ item }}"
  loop: "{{ some_list }}"

I am also aware of “search_string“, but we are running Ansible 2.9 currently which does not support this facility.

Advertisement

Answer

Try the filter regex_escape, e.g.

    - lineinfile:
        path: test.txt
        regexp: "^{{ item|regex_escape }}$"
        line: "{{ item }}"
      loop:
        - '^f.*o(.*)$'

gives

shell> cat test.txt
^f.*o(.*)$

Below is the Ansible(Python) equivalent of the Perl example

shell> cat test.yml
---
- hosts: localhost
  tasks:
    - lineinfile:
        path: test.txt
        regexp: "^{{ item.regexp }}$"
        line: "{{ item.line }}"
      loop:
        - {line: 'Foo (bar)', regexp: "{{ 'Foo ' ~ '(bar)'|regex_escape }}"}
        - {line: 'Foo (bar)', regexp: "{{ 'Fo. ' ~ '(bar)'|regex_escape }}"}
        - {line: 'Foo (bar)', regexp: "{{ 'Fo. ' ~ '(ba.)'|regex_escape }}"}

Running the playbook on an empty test.txt file in the check and diff mode gives

shell> ansible-playbook test.yml -CD

PLAY [localhost] ***************************************************

TASK [lineinfile] **************************************************
--- before: test.txt (content)
+++ after: test.txt (content)
@@ -0,0 +1 @@
+Foo (bar)

changed: [localhost] => (item={'line': 'Foo (bar)', 'regexp': 'Foo \(bar\)'})
--- before: test.txt (content)
+++ after: test.txt (content)
@@ -0,0 +1 @@
+Foo (bar)

changed: [localhost] => (item={'line': 'Foo (bar)', 'regexp': 'Fo. \(bar\)'})
--- before: test.txt (content)
+++ after: test.txt (content)
@@ -0,0 +1 @@
+Foo (bar)

changed: [localhost] => (item={'line': 'Foo (bar)', 'regexp': 'Fo. \(ba\.\)'})

The third item from the list is also reported changed despite the fact the regexp doesn’t match. Quoting from module lineinfile parameter regexp: “If the regular expression is not matched, the line will be added to the file …

The task is idempotent. If the line is present in the file

shell> cat test.txt
Foo (bar)

there are no changes

TASK [lineinfile] **************************************************
ok: [localhost] => (item={'line': 'Foo (bar)', 'regexp': 'Foo \(bar\)'})
ok: [localhost] => (item={'line': 'Foo (bar)', 'regexp': 'Fo. \(bar\)'})
ok: [localhost] => (item={'line': 'Foo (bar)', 'regexp': 'Fo. \(ba\.\)'})
User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement