Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Either the YAML parser or shell module insert leading whitespace on non-first lines #12034

Closed
garthk opened this issue Aug 21, 2015 · 8 comments
Labels
affects_2.1 This issue/PR affects Ansible v2.1 affects_2.3 This issue/PR affects Ansible v2.3 bug This issue/PR relates to a bug. has_pr This issue has an associated PR. pending_action support:core This issue/PR relates to code supported by the Ansible Engineering Team.

Comments

@garthk
Copy link

garthk commented Aug 21, 2015

ISSUE TYPE

Bug Report

COMPONENT NAME

core

ANSIBLE VERSION

2.1

CONFIGURATION
OS / ENVIRONMENT
SUMMARY

Demo:

  • Copy the following to leading-whitespace-demo.yaml
  • ansible-playbook -vi localhost, leading-whitespace-demo.yaml
#!/usr/bin/env ansible-playbook -vi localhost,
# leading-whitespace-demo.yaml

---
- name: demonstrate leading whitespace
  hosts: localhost
  connection: local
  gather_facts: no
  sudo: no

  tasks:

  - name: attempt
    shell: |
      echo first
      echo second

Output:

PLAY [demonstrate leading whitespace] ***************************************** 

TASK: [attempt] *************************************************************** 
changed: [localhost] => {"changed": true, "cmd": "echo first\n echo second", "delta": "0:00:00.005381", "end": "2015-08-21 16:06:50.534878", "rc": 0, "start": "2015-08-21 16:06:50.529497", "stderr": "", "stdout": "first\nsecond", "warnings": []}

PLAY RECAP ******************************************************************** 
localhost                  : ok=1    changed=1    unreachable=0    failed=0   

Note cmd is "echo first\n echo second": a space has been inserted at the beginning of the second line.

Run the same YAML through yaml.load, and there's no extra space:

$ python
Python 2.7.10 (default, Jul 14 2015, 19:46:27) 
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pprint, yaml
>>> pprint.pprint(yaml.load(file('leading-whitespace-demo.yaml').read()))
[{'connection': 'local',
  'gather_facts': False,
  'hosts': 'localhost',
  'name': 'demonstrate leading whitespace',
  'sudo': False,
  'tasks': [{'name': 'attempt', 'shell': 'echo first\necho second\n'}]}]
>>> ^D

Reproduced on Mac OS X and CentOS with Ansible 1.9.2.

STEPS TO REPRODUCE
EXPECTED RESULTS
ACTUAL RESULTS
@chrrrles
Copy link
Contributor

This behavior is not from shell, but from AnsibleModule's run_command method which quotes and sanitizes command arguments as a list before joining them together again as a space delimited string.

Removing P3 tag pending further discussion as to whether this is expected behavior for the shell module as it was not designed for batching multiple commands. This behavior is also exhibited by the raw module; for where it is may be more arguable label it as a bug:

[user@foo 12034]$ ansible-playbook -vvvi localhost, leading-whitespace-demo-with-raw.yaml                                                                                              
No config file found; using defaults
1 plays in leading-whitespace-demo-with-raw.yaml

PLAY [demonstrate leading whitespace] ******************************************

TASK [attempt] *****************************************************************
ESTABLISH LOCAL CONNECTION FOR USER: user
localhost EXEC echo first
 echo second
ok: [localhost] => {"changed": false, "rc": 0, "stderr": "", "stdout": "first\nsecond\n", "stdout_lines": ["first", "second"]}

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0   

@ansibot ansibot added the affects_2.1 This issue/PR affects Ansible v2.1 label Sep 8, 2016
@ansibot ansibot added the affects_2.3 This issue/PR affects Ansible v2.3 label Dec 13, 2016
robnagler added a commit to radiasoft/ansible-conf that referenced this issue Mar 16, 2017
@ansibot ansibot added needs_info This issue requires further information. Please answer any outstanding questions. needs_template This issue/PR has an incomplete description. Please fill in the proposed template correctly. module This issue/PR relates to a module. and removed needs_info This issue requires further information. Please answer any outstanding questions. needs_template This issue/PR has an incomplete description. Please fill in the proposed template correctly. labels Apr 4, 2017
@bugchecker
Copy link
Contributor

bugchecker commented Apr 11, 2017

I vote to allow multiline.

Some languages (like python) cannot be reduced to single line form:

- name: Ensure that tokens are NFC-normalized
  shell: |
    from unicodedata import normalize
    if "{{ item }}" != normalize("NFC", "{{ item }}"):
      raise RuntimeError("Token {{ item }} isn't in NFC form")
  args:
    executable: python3
  with_items: ["être", "мёд", "mädchen"]
  delegate_to: localhost

Multiline scripts can keep atomic (perform a single action):

- name: Wait until the socket opens
  shell: |
    import socket, time
    from contextlib import closing, suppress
    connected = False
    with closing(socket.socket()) as sock:
      while not connected:
        with suppress(ConnectionAbortedError, ConnectionRefusedError):
          sock.connect(('localhost', 8080))
          connected = True
        time.sleep(.5)
  args:
    executable: python3

Also difference between command and shell notations may be more clearly.

Python example:

subprocess.run(["ls", "."])
subprocess.run("ls .", shell=True)

Dockerfile example:

CMD ["ls", "."]
CMD ls .

Ansible example (how could it be):

- command: ["ls", "."]
- command: 
    - ls
    - .
- shell: ls .

@ansibot ansibot added the support:core This issue/PR relates to code supported by the Ansible Engineering Team. label Jun 29, 2017
@ansibot ansibot removed the module This issue/PR relates to a module. label Nov 18, 2017
@rgm3
Copy link
Contributor

rgm3 commented Feb 22, 2018

This breaks here document use in the shell module. For example:

- name: set some network iface file properties with ansible
  shell: |
    augtool -LA -t 'Shellvars incl {{ifcfg_dir}}/ifcfg-*' <<EOAUG
    set /files{{ifcfg_dir}}/ifcfg-{{item}}/ONBOOT "yes"
    set /files{{ifcfg_dir}}/ifcfg-{{item}}/BOOTPROTO "none"
    set /files{{ifcfg_dir}}/ifcfg-{{item}}/NM_CONTROLLED "no"
    save
    EOAUG
  register: aug
  changed_when: aug.stdout|search("Saved")
  with_items: "{{ ansible_interfaces|difference(['lo']) }}"

The end-of-document marker will never be hit because a space is inserted before.

@abadger
Copy link
Contributor

abadger commented Jun 22, 2018

It appears that we already discussed this in a parallel issue/PR and decided the recommended practice should be to use the script module instead. That resulted in us adding a note to use the script module to the docs: #32863

We'll discuss this in the meeting and if consensus hasn't changed, close this issue then.

@quixoten
Copy link
Contributor

That resulted in us adding a note to use the script module to the docs

Does the script module interpolate jinja as in @rgm3 example above? I tried converting my heredoc script to using the script module, but it doesn't look like it's parsing jinja. If the script module doesn't interpolate jinja, then getting the same functionality requires the template module, the shell module, and the file module (create script, run script, cleanup script). In previous ansible versions, this only required the shell module.

@phemmer
Copy link
Contributor

phemmer commented Jan 12, 2019

Just hit this myself after upgrading from ansible 1.9. The proposal of using script is obnoxious as you now have to use multiple files (plus the already-mentioned template issue).

However it appears there is a really simple workaround. Just put the content in a variable instead. For some reason it seems the leading newline is only added when you assign directly to the script value.

- hosts: localhost
  tasks:
    - shell: |
        echo first
        echo second

    - shell: '{{content}}'
      vars:
        content: |
          echo first
          echo second
TASK [shell] **************************************************************************************************
changed: [localhost] => {"changed": true, "cmd": "echo first\n echo second", "delta": "0:00:00.002009", "end": "2019-01-12 01:10:27.909195", "rc": 0, "start": "2019-01-12 01:10:27.907186", "stderr": "", "stderr_lines": [], "stdout": "first\nsecond", "stdout_lines": ["first", "second"]}

TASK [shell] **************************************************************************************************
changed: [localhost] => {"changed": true, "cmd": "echo first\necho second\n", "delta": "0:00:00.002110", "end": "2019-01-12 01:10:28.059276", "rc": 0, "start": "2019-01-12 01:10:28.057166", "stderr": "", "stderr_lines": [], "stdout": "first\nsecond", "stdout_lines": ["first", "second"]}

@ansibot ansibot added the has_pr This issue has an associated PR. label Jul 23, 2019
@ansibot ansibot added the needs_triage Needs a first human triage before being processed. label May 17, 2020
@mkrizek mkrizek removed the needs_triage Needs a first human triage before being processed. label May 18, 2020
@samdoran
Copy link
Contributor

samdoran commented May 7, 2021

This is resolved in newer Ansible versions.

@samdoran samdoran closed this as completed May 7, 2021
@bcoca
Copy link
Member

bcoca commented May 7, 2021

    "changed": true,
    "cmd": "echo first\necho second\n",
    "delta": "0:00:00.002033",
    "end": "2021-05-07 11:12:06.966239",
    "invocation": {
        "module_args": {
            "_raw_params": "echo first\necho second\n",
            "_uses_shell": true,
            "argv": null,
            "chdir": null,
            "creates": null,
            "executable": null,
            "removes": null,
            "stdin": null,
            "stdin_add_newline": true,
            "strip_empty_ends": true,
            "warn": false
        }
    },
    "msg": "",
    "rc": 0,
    "start": "2021-05-07 11:12:06.964206",
    "stderr": "",
    "stderr_lines": [],
    "stdout": "first\nsecond",
    "stdout_lines": [
        "first",
        "second"
    ]
}```

@ansible ansible locked and limited conversation to collaborators Jun 4, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
affects_2.1 This issue/PR affects Ansible v2.1 affects_2.3 This issue/PR affects Ansible v2.3 bug This issue/PR relates to a bug. has_pr This issue has an associated PR. pending_action support:core This issue/PR relates to code supported by the Ansible Engineering Team.
Projects
None yet
Development

No branches or pull requests