<p>Put the new items into a list. For example,</p>
<pre class="lang-yaml prettyprint-override"><code>  cmdline_add:
    - cgroup_enable=cpuset
    - cgroup_enable=memory
    - cgroup_memory=1
    - swapaccount=1
</code></pre>
<p>Add the items in the loop</p>
<pre class="lang-yaml prettyprint-override"><code>  - lineinfile:
      path: /boot/firmware/cmdline.txt
      regexp: '^(?!.*?{{ item }})(.*)$'
      line: '\1 {{ item }}'
      backrefs: true
    loop: &quot;{{ cmdline_add }}&quot;
</code></pre>
<p>Because of the negative lookahead assertion <em>(?!...)</em> the <em>regexp</em> matches if the <em>item</em> is not in the line. The task is idempotent.</p>
<hr>
<sup>
<p><strong>Example</strong>. Consolidate the configuration into comma-separated lists</p>
<p>Given the file</p>
<p>shell&gt; cat /tmp/cmdline.txt</p>
<blockquote>
<p>console=serial0,115200 dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash</p>
</blockquote>
<p>The play</p>
<pre class="lang-yaml prettyprint-override"><code>- hosts: localhost

  vars:

    cmdline_path: /tmp/cmdline.txt
    cmdline_add:
      - cgroup_enable=cpuset
      - cgroup_enable=memory
      - cgroup_memory=1
      - swapaccount=1

    cmdline_add_items: &quot;{{ cmdline_add|map('split', '=', 1) }}&quot;
    cmdline_add_list: |
      {% filter from_yaml %}
      {% for i in cmdline_add_items %}
      - {key: {{ i.0 }}, value: {{ i.1|d('')|split(',') }} }
      {% endfor %}
      {% endfilter %}

    cmdline_items: &quot;{{ cmdline_out.stdout|trim|split(' ')|map('split', '=', 1) }}&quot;
    cmdline_list: |
      {% filter from_yaml %}
      {% for i in cmdline_items %}
      - {key: {{ i.0 }}, value: {{ i.1|d('')|split(',') }} }
      {% endfor %}
      {% endfilter %}
     
    cmdline_dict: &quot;{{ [cmdline_list, cmdline_add_list]|
                      community.general.lists_mergeby('key', list_merge='append_rp')|
                      items2dict }}&quot;
    cmdline: &gt;-
      {% for k,v in cmdline_dict.items() -%}
      {% if v.0|length &gt; 0 -%}
      {{ k }}={{ v|join(',') }}
      {% else -%}
      {{ k }}
      {% endif -%}
      {% endfor -%}

  tasks:

    - command: &quot;cat {{ cmdline_path }}&quot;
      register: cmdline_out

    - block:
        - debug:
            var: cmdline_add_items|to_yaml
        - debug:
            var: cmdline_add_list|to_yaml
        - debug:
            var: cmdline_items|to_yaml
        - debug:
            var: cmdline_list|to_yaml
        - debug:
            var: cmdline_dict|to_yaml
        - debug:
            var: cmdline
      when: debug|d(false)|bool

    - copy:
        dest: &quot;{{ cmdline_path }}&quot;
        content: &quot;{{ cmdline }}&quot;
</code></pre>
<p>gives</p>
<p>shell&gt; cat /tmp/cmdline.txt</p>
<blockquote>
<p>cgroup_enable=cpuset,memory cgroup_memory=1 console=serial0,115200,tty1 dwc_otg.lpm_enable=0 fixrtc quiet root=LABEL=writable rootfstype=ext4 rootwait splash swapaccount=1</p>
</blockquote>
</sup>

CC BY-SA 4.0
2023-07-03T20:26:53.633Z
That's a good solution, thank you. I still don't follow why my task fails.
Jehan Alvani
CC BY-SA 4.0
2023-07-03T21:32:01.587Z
Probably because the first group is greedy `(.*)`. Try `(.*?)`. I haven't tested your pattern. There are more problems like `$1`.
Vladimir Botka
CC BY-SA 4.0
2023-07-03T20:09:25.260Z
2023-07-03T23:05:15.483Z
2023-07-03T23:05:15.483Z
Vladimir Botka
<p>I'm attempting to update <code>/boot/firmware/cmdline.txt</code> on a few Raspberry Pis to enable <code>cgroup</code> support.  Seemed a good fit for <code>ansible.builtin.lineinfile</code>.</p>
<p>The existing contents of <code>/boot/firmware/cmdline.txt</code> are a single line of key-value pairs</p>
<pre><code>console=serial0,115200 dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash
</code></pre>
<p>I want the following key-value pairs to be appended to any existing contents of that file that might exist.</p>
<ul>
<li><code>cgroup_enable=cpuset</code></li>
<li><code>cgroup_enable=memory</code></li>
<li><code>cgroup_memory=1</code></li>
<li><code>swapaccount=1</code></li>
</ul>
<p>In the end, I want the file to look like</p>
<pre><code>console=serial0,115200 dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1 swapaccount=1

</code></pre>
<p>Here's the task I created:</p>
<pre><code>- name: &quot;Modify /boot/firmware/cmdline.txt to enable cGroup support if not already enabled&quot;
    ansible.builtin.lineinfile:
      path: /boot/firmware/cmdline.txt
      regexp: '(.*) ((cgroup_enable=cpuset|cgroup_enable=memory|cgroup_memory=1|swapaccount=1)( {0,}|\n)){4}(.*)'
      line: '$1 cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1 swapaccount=1 $5'
      state: present
      backrefs: true
      backup: true
      owner: root
      group: root
      mode: '0755'
</code></pre>
<p>Unfortunately, it doesn't seem to update the file and I'm struggling to understand why. To clarify, the task executes fine but indicates that the file already matches the regexp with the following output:</p>
<pre><code>TASK [common : Modify /boot/firmware/cmdline.txt to enable cGroup support if not already enabled] **************************
ok: [kube02.local]
ok: [kube03.local]
ok: [kube01.local]
</code></pre>
<p>As far as I can tell, it does not and I've tested in a few ways. Increasing verbosity of this doesn't help me figure this out, unfortunately.</p>
<p>I did find <a href="https://stackoverflow.com/a/67649971">this post's answer</a> which has a more robust solution, but I would love to understand why my solution <em>doesn't</em> work.</p>

<ansible>
Why doesn't ansible.builtin.lineinfile behave as expected in this particular task?
Jehan Alvani
# Why doesn't ansible.builtin.lineinfile behave as expected in this particular task?

(Asked by: username_0 on 2023-07-03)

I'm attempting to update /boot/firmware/cmdline.txt on a few Raspberry Pis to enable cgroup support.  Seemed a good fit for ansible.builtin.lineinfile.
The existing contents of /boot/firmware/cmdline.txt are a single line of key-value pairs
console=serial0,115200 dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash

I want the following key-value pairs to be appended to any existing contents of that file that might exist.

cgroup_enable=cpuset
cgroup_enable=memory
cgroup_memory=1
swapaccount=1

In the end, I want the file to look like
console=serial0,115200 dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1 swapaccount=1


Here's the task I created:
- name: "Modify /boot/firmware/cmdline.txt to enable cGroup support if not already enabled"
    ansible.builtin.lineinfile:
      path: /boot/firmware/cmdline.txt
      regexp: '(.*) ((cgroup_enable=cpuset|cgroup_enable=memory|cgroup_memory=1|swapaccount=1)( {0,}|\n)){4}(.*)'
      line: '$1 cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1 swapaccount=1 $5'
      state: present
      backrefs: true
      backup: true
      owner: root
      group: root
      mode: '0755'

Unfortunately, it doesn't seem to update the file and I'm struggling to understand why. To clarify, the task executes fine but indicates that the file already matches the regexp with the following output:
TASK [common : Modify /boot/firmware/cmdline.txt to enable cGroup support if not already enabled] **************************
ok: [kube02.local]
ok: [kube03.local]
ok: [kube01.local]

As far as I can tell, it does not and I've tested in a few ways. Increasing verbosity of this doesn't help me figure this out, unfortunately.
I did find this post's answer which has a more robust solution, but I would love to understand why my solution doesn't work.

**Tags:** ansible


---

## 1 answers

### Answer by username_1 on 2023-07-03. Score: 1

Put the new items into a list. For example,
  cmdline_add:
    - cgroup_enable=cpuset
    - cgroup_enable=memory
    - cgroup_memory=1
    - swapaccount=1

Add the items in the loop
  - lineinfile:
      path: /boot/firmware/cmdline.txt
      regexp: '^(?!.*?{{ item }})(.*)$'
      line: '\1 {{ item }}'
      backrefs: true
    loop: "{{ cmdline_add }}"

Because of the negative lookahead assertion (?!...) the regexp matches if the item is not in the line. The task is idempotent.


Example. Consolidate the configuration into comma-separated lists
Given the file
shell> cat /tmp/cmdline.txt

console=serial0,115200 dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash

The play
- hosts: localhost

  vars:

    cmdline_path: /tmp/cmdline.txt
    cmdline_add:
      - cgroup_enable=cpuset
      - cgroup_enable=memory
      - cgroup_memory=1
      - swapaccount=1

    cmdline_add_items: "{{ cmdline_add|map('split', '=', 1) }}"
    cmdline_add_list: |
      {% filter from_yaml %}
      {% for i in cmdline_add_items %}
      - {key: {{ i.0 }}, value: {{ i.1|d('')|split(',') }} }
      {% endfor %}
      {% endfilter %}

    cmdline_items: "{{ cmdline_out.stdout|trim|split(' ')|map('split', '=', 1) }}"
    cmdline_list: |
      {% filter from_yaml %}
      {% for i in cmdline_items %}
      - {key: {{ i.0 }}, value: {{ i.1|d('')|split(',') }} }
      {% endfor %}
      {% endfilter %}
     
    cmdline_dict: "{{ [cmdline_list, cmdline_add_list]|
                      community.general.lists_mergeby('key', list_merge='append_rp')|
                      items2dict }}"
    cmdline: >-
      {% for k,v in cmdline_dict.items() -%}
      {% if v.0|length > 0 -%}
      {{ k }}={{ v|join(',') }}
      {% else -%}
      {{ k }}
      {% endif -%}
      {% endfor -%}

  tasks:

    - command: "cat {{ cmdline_path }}"
      register: cmdline_out

    - block:
        - debug:
            var: cmdline_add_items|to_yaml
        - debug:
            var: cmdline_add_list|to_yaml
        - debug:
            var: cmdline_items|to_yaml
        - debug:
            var: cmdline_list|to_yaml
        - debug:
            var: cmdline_dict|to_yaml
        - debug:
            var: cmdline
      when: debug|d(false)|bool

    - copy:
        dest: "{{ cmdline_path }}"
        content: "{{ cmdline }}"

gives
shell> cat /tmp/cmdline.txt

cgroup_enable=cpuset,memory cgroup_memory=1 console=serial0,115200,tty1 dwc_otg.lpm_enable=0 fixrtc quiet root=LABEL=writable rootfstype=ext4 rootwait splash swapaccount=1

> **Comments:**
> **username_0**: That's a good solution, thank you. I still don't follow why my task fails.  
> **username_1**: Probably because the first group is greedy `(.*)`. Try `(.*?)`. I haven't tested your pattern. There are more problems like `$1`.  

---

