Reputation: 2830
I have the same question as in Multiline strings with leading spaces, but the solutions I found there don't work for the lineinfile
module I'm currently using to append to an existing text file in remote host.
Idea is to write a log with bullet list, i.e. an output file like this. It's created line by line in different tasks in the play.
Start
- perform X
+ perform X1
- perform Y
End
I tried literally everything and guess this is possible in YAML (https://yaml-multiline.info/), but Ansible is breaking it again like it did before (https://github.com/ansible/ansible/issues/12133). Anybody aware of any workaround?
Here are two attempts I guess should work from YAML side. First one is even a syntax error for Ansible.
- name: append log
lineinfile:
path: "{{ log_path }}"
line: |6
- perform X
+ perform X1
- name: append log
lineinfile:
path: "{{ log_path }}"
line: "\
- perform X\n
+ perform X1\n"
Output in the file in the second has leading space stripped and indention doesn't match the other bullet points from single line strings:
Start
- perform X
+ perform X1
- perform Y
End
Upvotes: 1
Views: 1126
Reputation: 12090
It is still unclear for me what you try to achieve. One point is idempotency, an other point is a mutually exclusive problem description. However,
I need to append text to a file. How do you do this with these modules?
from your description and example I understand that you have a file
Input (input.file
)
Start
- perform Y
End
Annot.: A protocol or an algorithm
in which you like to insert lines before and expect a result like
Output / Result (output.file
)
Start
- perform X
+ perform X1
- perform Y
End
Annot.: Insert before performing Y
because of the given examples. Not to append in any case as it looks like for me that Start
and End
are markers within the file and part of the protocol. Furthermore an algorithm or protocol depends on order otherwise it would be a log file.
One option for processing could be to patch the file.
diff input.file output.file > input.diff
cat input.diff
1a2,3
> - perform X
> + perform X1
Processing
So an example playbook like
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: Apply patch to a file
patch:
src: input.diff
dest: "/home/{{ ansible_user }}/test/output.file"
will result into the necessary output.
"It is created in the play at different stages line by line. In one situation I would like to add a multiline string in one append operation."
If you like to write a protocol about what happened, also know as log file and to append raw strings to an existing (log) file you may have a look into the following approach with as !unsafe
marked strings
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: Append (unsafe) lines to log
lineinfile:
path: log.file
create: true
line: !unsafe >-
test
- test
+ test
resulting into a file with a content of
cat log.file
test
- test
+ test
test
- test
+ test
test
- test
+ test
after three (3) iterations.
Ansible uses a data type called
unsafe
to block templating. ... The Ansible implementation ensures that unsafe values are never templated.
This will not only give the possibility to have leading whitespace(s) but also allow special characters like {
or %
.
Upvotes: 1
Reputation: 39169
It is unclear to me if you want to achieve idempotency or not.
In the case you want to achieve idempotency, the lineinfile
module is not the proper one to use, as it is meant to change a single line, not multiple ones:
- This is primarily useful when you want to change a single line in a file only.
- See the
ansible.builtin.replace
module if you want to change multiple, similar lines or checkansible.builtin.blockinfile
if you want to insert/update/remove a block of lines in a file. For other cases, see theansible.builtin.copy
or ansible.builtin.template modules.
Source: lineinfile
module synopsis
But, as far as your indentation matter is concerned, then you could use the indent
filter of Jinja and place your desired lines in the vars
of your module. You will also need the second parameter of the indent
filter set to true
, as you also want to indent the first line:
- name: append protocol
lineinfile:
path: "{{ protocol_path }}"
line: "{{ _line | indent(1, true) }}"
vars:
_line: |
- perform X
+ perform X1
This will render in:
- perform X
+ perform X1
Upvotes: 2
Reputation: 2830
I found one workaround that uses single line strings and a loop, i.e. I document it here, but would still prefer to have a solution for the general real multiline case (e.g. from a variable):
- name: append protocol
lineinfile:
path: "{{ protocol_path }}"
line: "{{ item }}"
loop:
- " - perform X"
- " + perform X1"
Upvotes: 0