Complex lineinfile/replace

I need to replace lines in /etc/fstab using the following logic:

if line contains “ext4”; then
if line ends in “0 0”; then
replace “0 0” with “1 2”

I can easily do this with sed:
sed ‘/ext4/s/0 0$/1 2/’

But from what I can tell the lineinfile and replace modules accept only one regexp, not two. How would I do this in ansible without using the script or command modules?

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

# See [https://docs.python.org/3/library/re.html](https://docs.python.org/3/library/re.html) for further details on syntax
- name: Use backrefs with alternative group syntax to avoid conflicts with variable values
  ansible.builtin.lineinfile:
    path: /tmp/config
    regexp: ^(host=).*
    line: \g<1>{{ hostname }}
    backrefs: yes

Can you use backrefs to incorporate part of the found regexp in the new line?

ansible.builtin.lineinfile:
path: /etc/fstab
backrefs: yes
regexp: “(ext4.)\s+0 0\s$”
line: “\g<1> 1 2”

Just a stab in the dark. Have not tested this code.

Walter

Actually a little more improvement to get the whole line from the regex minus the “0 0” tail.

ansible.builtin.lineinfile:
path: /etc/fstab
backrefs: yes
regexp: “^(.ext4.)\s+0 0\s*$”
line: “\g<1> 1 2”

Walter

Ahh yes, python regular expressions. Thank you.

Managed to get it working. It only affects the last matched instance in the file. Would the replace module be a better choice?

Yes, replace would be better if you have multiple instances in a file and want to modify all of them.

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

Walter

ansible.builtin.replace:
path: /etc/fstab
regexp: “^(.ext4.)\s+0 0\s*$”
replace: “\g<1> 1 2”

Walter

It needs to be ‘whitespace’ext4’whitespace’ unless you want to change mount points that might contain the letters ext4.

Are you sure you want to change the root filesystem to ‘1 2’

Man Page: The root filesystem should be specified with a fs_passno of 1

Regards,

Stan

Good catch!

ansible.builtin.replace:
  path: /etc/fstab
  regexp: "^(.*\s+ext4\s+.*)\s+0 0\s*$"
  replace: "\g<1> 1 2"

Walter

Thank you, Walter. I always enjoy reading your solutions.

This is where exactness matters and can become a rabbit hole very quickly depending on how much of your environment is a wild wild west.

SUCCENCT DEFINITION: The match should be the 3rd argument (ARG3) in /etc/fstab equal to “ext4” of any lines not beginning with “#” with ARG5 and ARG6 = “0”.

Depending on the risk you’re willing to accept, /etc/fstab has six white-space delimited arguments.

ARG1\s+ARG2\s+ARG3\s+ARG4\s+ARG5\s+ARG6

When ARG3 = ext4 and ARG5 and ARG6 = 0

then

ARG1 ARG2 ext4 ARG4 1 2

backrefs will help keep it straight.

regexp: “^(.)\s+(.)\s+ext4\s+(0)\s+(0)$”

replace: “\1 \2 ext4 \4 1 2”

Do you agree this is more exact and better declaratively? Probably way more than Cy Schubert really wanted.

Regards,

Stan

You have to test it. The regex wants to match the longest string possible and .* matches everything. If the regex I typed works, go with it. You can overthink it and spend cycles trying to get something to work when a simpler regex might have been sufficient.

Walter