edit string in a multi-line file

I am trying to edit a string in a multi-line file. For instance, if I had this string:

This is a file and I am editing it

I want to add the string “new” in front of file if it does not exist already.

I’ve tried lineinfile, but the issue is that I don’t know what else is on the line. There could be more data after “This is a file and I am editing it” that I don’t want to change. What is the best way to do this?

would this do what you want?

  • name: edit a line in a file
    ansible.builtin.command:
    chdir: /home/username/
    cmd: ‘sed -i “s/This is a file/This is a new file/” somefile’

curious actually how this can be achieved with sophisticated regex and the lineinfile module

That will work - yes. I was so focused on using lineinfile or another ansible module, I forgot about sed. Thank you!

What you want to use is backrefs.

https://docs.ansible.com/ansible/latest/collections/ansible/builtin/lineinfile_module.html

An Example from the document:

# NOTE: Yaml requires escaping backslashes in double quotes but not in single quotes

- name**:** Ensure the JBoss memory settings are exactly as needed

ansible.builtin.lineinfile**:**

path**:** /opt/jboss-as/bin/standalone.conf

regexp**:** ‘^(.)Xms(\d+)m(.)$’

line**:** ‘\1Xms${xms}m\3’

backrefs**:** yes

Here’s one solution. It uses backrefs and a negative lookahead assertion.

- name: Test lineinfile
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Create a file to work on
      ansible.builtin.copy:
        content: |
           First line of a file
           Second line of the same file
           This is a file and I am editing it. Extra Stuff!
           Fourth line of this file
        dest: /tmp/textfile.txt

    - name: Show the file content
      ansible.builtin.debug:
        msg: "{{ lookup('file', '/tmp/textfile.txt') }}"

    - name: Insert "new" before "file" in the 3rd line
      ansible.builtin.lineinfile:
        path: /tmp/textfile.txt
        regexp: '(This is a )(?!>new)(file and I am editing it\..*)'
        backrefs: true
        line: 'new '

    - name: Show the file content a 2nd time
      ansible.builtin.debug:
        msg: "{{ lookup('file', '/tmp/textfile.txt') }}"

    - name: Second identical lineinfile should not make changes
      ansible.builtin.lineinfile:
        path: /tmp/textfile.txt
        regexp: '(This is a )(?!>new)(file and I am editing it\..*)'
        backrefs: true
        line: 'new '

    - name: Show the file content a 3nd time
      ansible.builtin.debug:
        msg: "{{ lookup('file', '/tmp/textfile.txt') }}"