198

right now I am using a shell script in ansible that would be much more readable if it was on multiple lines

- name: iterate user groups shell: groupmod -o -g {{ item['guid'] }} {{ item['username'] }} ....more stuff to do with_items: "{{ users }}" 

Just not sure how to allow multiline script in Ansible shell module

1
  • 2
    Also consider using the ansible 'script' command and use an external file Commented Jan 24, 2017 at 6:43

7 Answers 7

430

Ansible uses YAML syntax in its playbooks. YAML has a number of block operators:

  • The > is a folding block operator. That is, it joins multiple lines together by spaces. The following syntax:

     key: > This text has multiple lines 

    Would assign the value This text has multiple lines\n to key.

  • The | character is a literal block operator. This is probably what you want for multi-line shell scripts. The following syntax:

     key: | This text has multiple lines 

    Would assign the value This text\nhas multiple\nlines\n to key.

You can use this for multiline shell scripts like this:

- name: iterate user groups shell: | groupmod -o -g {{ item['guid'] }} {{ item['username'] }} do_some_stuff_here and_some_other_stuff with_items: "{{ users }}" 

(Update in 2024: the following is no longer true; Ansible is now less janky.)

There is one caveat: Ansible does some janky manipulation of arguments to the shell command, so while the above will generally work as expected, the following won't:

- shell: | cat <<EOF This is a test. EOF 

Ansible will actually render that text with leading spaces, which means the shell will never find the string EOF at the beginning of a line. You can avoid Ansible's unhelpful heuristics by using the cmd parameter like this:

- shell: cmd: | cat <<EOF This is a test. EOF 

@JKLaiho points out in a comment that the behavior of > is perhaps unexpected if you include additional indentation in your string. If you write:

key: > this is a test 

You will get the value:

"this\n is\n a\n test\n" 
Sign up to request clarification or add additional context in comments.

4 Comments

What if using ansible instead of ansiable-playbook?
Using shell: > actually results in ansible inserting line breaks, so this is incorrect.
Nowhere in this answer do I show an example of using shell: >, and furthermore your statement is simply incorrect: Ansible doesn't know whether you use " or > or |; it only knows what text it receives after parsing the YAML. The > quote operating is a folding operator and does not preserve newlines. If you're seeing different behavior, feel free to open a question and I would be happy to take a look at it.
There's a slight gotcha about the indentation of the rows beneath >: they all must be indented the same amount for the folding to work correctly (at least in Ansible). I posted a complementary answer about this. This might be what @PhilippLudwig was seeing?
26

Tried with ansible 2.0.0.2:

--- - hosts: all tasks: - name: multiline shell command shell: > ls --color /home register: stdout - name: debug output debug: msg={{ stdout }} 

The shell command is collapsed into a single line, as in ls --color /home

Reference (visited in 2021): https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html ==> search form "multiple lines" in the page.

3 Comments

Yeah but in the shell > has a very specific meaning. I tried this and it did not work as expected.
That is why it's just in the first line, not in the subsequent ones. It worked fine for me with ansible 2.0 as I wrote, although it didn't print out the full ls output with ansible 1.9.4. What version of Ansible did you use?
@MarcelloRomani It does not work with ansible 2.10.8.
8

Adding a space before the EOF delimiter allows to avoid cmd:

- shell: | cat <<' EOF' This is a test. EOF 

Comments

8

I prefer this syntax as it allows to set configuration parameters for the shell:

--- - name: an example shell: cmd: | docker build -t current_dir . echo "Hello World" date chdir: /home/vagrant/ 

Comments

8

A quick caveat about the folding > operator that had me scratching my head for a while: the rows indented beneath it have to be at the same indentation level for the folding to happen correctly, at least when using Ansible.

I was converting an existing multi-line shell command that had \ line endings to escape newlines and had indented every row below the first one two spaces more. I used shell: >, removed the backslashes and preserved the existing indentation.

The newlines were preserved, and I was losing my mind, because everywhere it said that > should convert them to spaces.

See below for an example:

# Converts into ls -la /tmp, as expected - name: working shell command that folds newlines shell: > ls -la /tmp # Tries to run -la and /tmp as their own commands - name: failing shell command that does not fold newlines shell: > ls -la /tmp 

Hope this helps someone in a similar situation!

Comments

1

You can use lineinfile collection to append the file, attribute create to create file if not exist and multi line string using "|".

- name: Add a line to a file if the file does not exist ansible.builtin.lineinfile: path: ~/.ssh/config line: | Host {{item.COMPONENT}} {{item.COMPONENT}}.{{ZONE}} HostName {{r53_var_one.resource_record_sets[0].resource_records[0].value}} User centos Port 22 IdentityFile ~/.ssh/key.pem StrictHostKeyChecking no create: yes mode: '0600' 

Comments

1

To know how to multilines works in ansbile, you may want to test this playbook to actually know what pipe (LF->LF) and Greather than (LF->space) are about.

- hosts: all,localhost tasks: - name: greather_than debug: msg: > a b c - name: pipe debug: msg: | a b c 

This is tested this way.

# ansible-playbook test.yml [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all' PLAY [all,localhost] *************************************************************************** TASK [Gathering Facts] ************************************************************************* ok: [localhost] TASK [greater_than] **************************************************************************** ok: [localhost] => { "msg": "a b c\n" } TASK [pipe] *********************************************************************************** ok: [localhost] => { "msg": "a \nb\nc\n" } PLAY RECAP *********************************************************************************** localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 # 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.