108

I tried this:

- command: ./configure chdir=/src/package/
- command: /usr/bin/make chdir=/src/package/
- command: /usr/bin/make install chdir=/src/package/

which works, but I was hoping for something neater.

So I tried this:

from: https://stackoverflow.com/questions/24043561/multiple-commands-in-the-same-line-for-bruker-topspin which give me back "no such file or directory"

- command: ./configure;/usr/bin/make;/usr/bin/make install chdir=/src/package/

I tried this too: https://u.osu.edu/hasnan.1/2013/12/16/ansible-run-multiple-commands-using-command-module-and-with-items/

but I couldn't find the right syntax to put:

- command: "{{ item }}" chdir=/src/package/
  with_items:
      ./configure
      /usr/bin/make
      /usr/bin/make install

That does not work, saying there is a quote issue.

AntonioK
  • 440
  • 6
  • 20
John Doe
  • 1,570
  • 3
  • 13
  • 22

6 Answers6

142

To run multiple shell commands with ansible you can use the shell module with a multi-line string (note the pipe after shell:), as shown in this example:

  - name: Build nginx 
    shell: |
      cd nginx-1.11.13
      sudo ./configure
      sudo make
      sudo make install
QA Collective
  • 2,222
  • 21
  • 34
Arvik
  • 2,524
  • 2
  • 15
  • 10
  • thanks. is there a way to tell it to ignore failures and continue with subsequent commands in the list? – sogwiz Oct 25 '18 at 22:53
127

If a value in YAML begins with a curly brace ({), the YAML parser assumes that it is a dictionary. So, for cases like this where there is a (Jinja2) variable in the value, one of the following two strategies needs to be adopted to avoiding confusing the YAML parser:

Quote the whole command:

- command: "{{ item }} chdir=/src/package/"
  with_items:
  - ./configure
  - /usr/bin/make
  - /usr/bin/make install    

or change the order of the arguments:

- command: chdir=/src/package/ {{ item }}
  with_items:
  - ./configure
  - /usr/bin/make
  - /usr/bin/make install

Thanks for @RamondelaFuente alternative suggestion.

Pedro Romano
  • 10,973
  • 4
  • 46
  • 50
  • You do need to quote the variables in some situations, like when they contain spaces, for example. – Pedro Romano Jul 23 '14 at 14:01
  • 5
    You need the double quotes around the entire command. The problem lies with the first character of the value being a "{" which has meaning in YAML. So it's - command: "{{ item }} chdir=/src/package/" – Ramon de la Fuente Jul 27 '14 at 11:59
  • what if you have a variable in one of your items? ie - cp {{ base_dir }} – 4m1r Feb 04 '15 at 21:21
  • The safe option with any string (including ones with variables) is to simply quote the whole string: `"cp {{base_dir}}"`. – Pedro Romano Feb 06 '15 at 09:11
12

Shell works for me.

Simply to say, Shell is the same as you run a shell script.

Notes:

  1. Make sure use | when running multiple cmds.
  2. Shell won't return errors if the last cmd is success (just like normal shell)
  3. Control it with exit 0/1 if you want to stop ansible when error occurs.

The following example shows an error in shell, but it's success at the end of the execution.

- name: test shell with an error
become: no
shell: |
  rm -f /test1  # This should be an error.
  echo "test2"
  echo "test1"
  echo "test3" # success

This example shows stopinng shell with exit 1 error.

- name: test shell with exit 1
become: no
shell: |
  rm -f /test1  # This should be an error.
  echo "test2"
  exit 1        # this stops ansible due to returning an error
  echo "test1"
  echo "test3" # success

reference: https://docs.ansible.com/ansible/latest/modules/shell_module.html

kalu Wang
  • 191
  • 1
  • 4
11

You can also do like this:

- command: "{{ item }}"
  args:
    chdir: "/src/package/"
  with_items:
    - "./configure"
    - "/usr/bin/make"
    - "/usr/bin/make install"

Hope that might help other

AntonioK
  • 440
  • 6
  • 20
Arbab Nazar
  • 22,378
  • 10
  • 76
  • 82
6

I faced the same issue. In my case, part of my variables were in a dictionary i.e. with_dict variable (looping) and I had to run 3 commands on each item.key. This solution is more relevant where you have to use with_dict dictionary with running multiple commands (without requiring with_items)

Using with_dict and with_items in one task didn't help as it was not resolving the variables.

My task was like:

- name: Make install git source
  command: "{{ item }}"
  with_items:
    - cd {{ tools_dir }}/{{ item.value.artifact_dir }}
    - make prefix={{ tools_dir }}/{{ item.value.artifact_dir }} all
    - make prefix={{ tools_dir }}/{{ item.value.artifact_dir }} install
  with_dict: "{{ git_versions }}"

roles/git/defaults/main.yml was:

---
tool: git
default_git: git_2_6_3

git_versions:
  git_2_6_3:
    git_tar_name: git-2.6.3.tar.gz
    git_tar_dir: git-2.6.3
    git_tar_url: https://www.kernel.org/pub/software/scm/git/git-2.6.3.tar.gz

The above resulted in an error similar to the following for each {{ item }} (for 3 commands as mentioned above). As you see, the values of tools_dir is not populated (tools_dir is a variable which is defined in a common role's defaults/main.yml and also item.value.git_tar_dir value was not populated/resolved).

failed: [server01.poc.jenkins] => (item=cd {# tools_dir #}/{# item.value.git_tar_dir #}) => {"cmd": "cd '{#' tools_dir '#}/{#' item.value.git_tar_dir '#}'", "failed": true, "item": "cd {# tools_dir #}/{# item.value.git_tar_dir #}", "rc": 2}
msg: [Errno 2] No such file or directory

Solution was easy. Instead of using "COMMAND" module in Ansible, I used "Shell" module and created a a variable in roles/git/defaults/main.yml

So, now roles/git/defaults/main.yml looks like:

---
tool: git
default_git: git_2_6_3

git_versions:
  git_2_6_3:
    git_tar_name: git-2.6.3.tar.gz
    git_tar_dir: git-2.6.3
    git_tar_url: https://www.kernel.org/pub/software/scm/git/git-2.6.3.tar.gz

#git_pre_requisites_install_cmds: "cd {{ tools_dir }}/{{ item.value.git_tar_dir }} && make prefix={{ tools_dir }}/{{ item.value.git_tar_dir }} all && make prefix={{ tools_dir }}/{{ item.value.git_tar_dir }} install"

#or use this if you want git installation to work in ~/tools/git-x.x.x
git_pre_requisites_install_cmds: "cd {{ tools_dir }}/{{ item.value.git_tar_dir }} && make prefix=`pwd` all && make prefix=`pwd` install"

#or use this if you want git installation to use the default prefix during make 
#git_pre_requisites_install_cmds: "cd {{ tools_dir }}/{{ item.value.git_tar_dir }} && make all && make install"

and the task roles/git/tasks/main.yml looks like:

- name: Make install from git source
  shell: "{{ git_pre_requisites_install_cmds }}"
  become_user: "{{ build_user }}"
  with_dict: "{{ git_versions }}"
  tags:
    - koba

This time, the values got successfully substituted as the module was "SHELL" and ansible output echoed the correct values. This didn't require with_items: loop.

"cmd": "cd ~/tools/git-2.6.3 && make prefix=/home/giga/tools/git-2.6.3 all && make prefix=/home/giga/tools/git-2.6.3 install",
AntonioK
  • 440
  • 6
  • 20
AKS
  • 16,482
  • 43
  • 166
  • 258
6

Here is worker like this. \o/

- name: "Exec items"
  shell: "{{ item }}"
  with_items:
    - echo "hello"
    - echo "hello2"
AntonioK
  • 440
  • 6
  • 20
psicopante
  • 353
  • 3
  • 5