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\.\)'})