Answers: <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>
  
Answers: CC BY-SA 4.0
Answers: 2023-07-03T20:26:53.633Z
Answers: That's a good solution, thank you. I still don't follow why my task fails.
Answers: Jehan Alvani
Answers: CC BY-SA 4.0
Answers: 2023-07-03T21:32:01.587Z
Answers: Probably because the first group is greedy `(.*)`. Try `(.*?)`. I haven't tested your pattern. There are more problems like `$1`.
Answers: Vladimir Botka
Answers: CC BY-SA 4.0
Answers: 2023-07-03T20:09:25.260Z
Answers: 2023-07-03T23:05:15.483Z
Answers: 2023-07-03T23:05:15.483Z
Answers: Vladimir Botka
Body: <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>
  
Tags: <ansible>
Title: Why doesn't ansible.builtin.lineinfile behave as expected in this particular task?
UserDisplayName: Jehan Alvani
ThreadText: # 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](https://stackoverflow.com/a/67649971) 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,
  
  ```yaml
    cmdline_add:
      - cgroup_enable=cpuset
      - cgroup_enable=memory
      - cgroup_memory=1
      - swapaccount=1
  
  ```
  
  Add the items in the loop
  
  ```yaml
    - 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
  
  ```yaml
  - 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`.  
  
  ---
  
  