Cannot use the backtick character in the ansible shell/script cmd without the quote filter

Some of our passwords contain the backtick character.
This issue prevents us from using them with the shell or script module when passed as parameter in a variable.

# ansible --version
ansible [core 2.21.2]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/ansible/venv/lib/python3.14/site-packages/ansible
  ansible collection location = /opt
  executable location = /opt/ansible/venv/bin/ansible
  python version = 3.14.4 (main, Apr  8 2026, 04:02:31) [GCC 15.2.0] (/opt/ansible/venv/bin/python3.14)
  jinja version = 3.1.6
  pyyaml version = 6.0.3 (with libyaml v0.2.5)

EDITED to reflect the modified title

- shell:
        cmd: |
                echo "'`'"
        executable: /bin/bash

leads to: unexpected EOF while looking for matching ``

[ERROR]: Task failed: Module failed: The command exited with a non-zero return code.
Origin: /roles/test/tasks/backtick.yml:36:3

34 ##################################################################################################################...
35
36 - shell:
     ^ column 3

Traceback (most recent call last):
  File "<stdin>", line 266, in <module>
  File "<stdin>", line 260, in _ansiballz_main
  File "<stdin>", line 140, in invoke_module
  File "/tmp/ansible_ansible.legacy.command_payload_2jqknp9u/ansible_ansible.legacy.command_payload.zip/ansible/module_utils/_internal/_ansiballz/_loader.py", line 35, in run_module
    _run_module(
  File "/tmp/ansible_ansible.legacy.command_payload_2jqknp9u/ansible_ansible.legacy.command_payload.zip/ansible/module_utils/_internal/_ansiballz/_loader.py", line 62, in _run_module
    runpy.run_module(mod_name=module_fqn, init_globals=init_globals, run_name='__main__', alter_sys=True)
  File "<frozen runpy>", line 226, in run_module
  File "<frozen runpy>", line 98, in _run_module_code
  File "<frozen runpy>", line 88, in _run_code
  File "/tmp/ansible_ansible.legacy.command_payload_2jqknp9u/ansible_ansible.legacy.command_payload.zip/ansible/modules/command.py", line 367, in <module>
    main()
  File "/tmp/ansible_ansible.legacy.command_payload_2jqknp9u/ansible_ansible.legacy.command_payload.zip/ansible/modules/command.py", line 361, in main
    module.fail_json(**r)
Message: The command exited with a non-zero return code.

The above target exception was the direct cause of the following controller exception:

Traceback (most recent call last):
  File "/opt/ansible/venv/lib/python3.14/site-packages/ansible/executor/task_executor.py", line 324, in _execute
    utr.maybe_raise_on_result()
    ~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/opt/ansible/venv/lib/python3.14/site-packages/ansible/_internal/_task.py", line 1263, in maybe_raise_on_result
    raise _captured.AnsibleResultCapturedError(self.exception.event, self)
ansible._internal._errors._captured.AnsibleResultCapturedError: Module failed: The command exited with a non-zero return code.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/opt/ansible/venv/lib/python3.14/site-packages/ansible/executor/task_executor.py", line 327, in _execute
    raise AnsibleTaskError(obj=self._task.get_ds()) from ex
ansible.errors.AnsibleTaskError: Task failed: Module failed: The command exited with a non-zero return code.

fatal: [test.example.com]: FAILED! => 
    changed: true
    cmd: |-
        echo "``` | quote "
    delta: '0:00:00.003953'
    end: '2026-08-04 18:32:32.684389'
    msg: The command exited with a non-zero return code.
    rc: 2
    start: '2026-08-04 18:32:32.680436'
    stderr: '/bin/bash: -c: line 1: unexpected EOF while looking for matching ``'''
    stderr_lines: <omitted>
    stdout: ''
    stdout_lines: <omitted>

Is there a way to workaround that issue?

ansible-core

This is not ansible problem.

In bash, backticks `...` perform command substitution: Bash runs the command inside them and replaces the backticks with its output.

echo "Today is `date +%A`"

So when you run in bash it waits for backtick to be closed - try it.

echo "` | quote "

You have to use literal string (single quotes ')

echo '` | quote '

My example has been truncated.
Actually, I meant:

echo "'{{ var_containing_backtick | quote }}'"

which leads to the issue.

Either let the quote filter quote it, or quote it yourself. But not both. Behold:

---
- name: Ansible quote games
  hosts: localhost
  gather_facts: false
  vars:
    var_containing_backtick: 'abcd`efgh'
  tasks:
    - name: Print data
      ansible.builtin.debug:
        msg: echo {{ var_containing_backtick | quote }}

    - name: Bash script with var_containing_backtick
      ansible.builtin.shell:  # noqa no-changed-when
        cmd: |
          echo {{ var_containing_backtick | quote }}
          bashvar={{ var_containing_backtick | quote }}
          echo ${bashvar:Q}
        executable: /bin/bash

Produces this:

TASK [Print data] **********************************
task path: /home/utoddl/ansible/test.yml:8
ok: [localhost] => 
    msg: echo 'abcd`efgh'

TASK [Bash script with var_containing_backtick] ****
task path: /home/utoddl/ansible/test.yml:12
changed: [localhost] => 
    changed: true
    cmd: |-
        echo 'abcd`efgh'
        bashvar='abcd`efgh'
        echo ${bashvar:Q}
    delta: '0:00:00.003199'
    end: '2026-08-04 14:10:18.818174'
    msg: ''
    rc: 0
    start: '2026-08-04 14:10:18.814975'
    stderr: ''
    stderr_lines: <omitted>
    stdout: |-
        abcd`efgh
        abcd`efgh
    stdout_lines: <omitted>

You’re right for the Not both part, except that the issue is still here if you do not use the quote filter:

- name: Ansible quote games
  hosts: localhost
  gather_facts: false
  vars:
        var_containing_backtick: '`'
  tasks:
        - shell:
                cmd: echo {{ var_containing_backtick | quote }}
                executable: /bin/bash

        - shell:
                cmd: echo "'{{ var_containing_backtick }}'"
                executable: /bin/bash

PLAY [Ansible quote games] ****************************************************************************************************************************************************************************************

TASK [shell] ******************************************************************************************************************************************************************************************************
task path: /playbooks/tests/backtik.yml:8
Wednesday 05 August 2026  14:55:31 +0200 (0:00:00.022)       0:00:00.022 ****** 
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: root
<localhost> EXEC /bin/bash -c 'echo ~root'
<localhost> EXEC /bin/bash -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp `"&& mkdir "` echo /root/.ansible/tmp/ansible-tmp-1785934531.1791315-2267715-152365080812646 `" && echo ansible-tmp-1785934531.1791315-2267715-152365080812646="` echo /root/.ansible/tmp/ansible-tmp-1785934531.1791315-2267715-152365080812646 `" )'
Using module file /opt/ansible/venv/lib/python3.14/site-packages/ansible/modules/command.py
<localhost> PUT /root/.ansible/tmp/ansible-local-2267613heyjogs3/tmpu5nan1pq TO /root/.ansible/tmp/ansible-tmp-1785934531.1791315-2267715-152365080812646/AnsiballZ_command.py
<localhost> EXEC /bin/bash -c 'chmod u+rwx /root/.ansible/tmp/ansible-tmp-1785934531.1791315-2267715-152365080812646/ /root/.ansible/tmp/ansible-tmp-1785934531.1791315-2267715-152365080812646/AnsiballZ_command.py'
<localhost> EXEC /bin/bash -c '/usr/bin/python3 /root/.ansible/tmp/ansible-tmp-1785934531.1791315-2267715-152365080812646/AnsiballZ_command.py'
<localhost> EXEC /bin/bash -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1785934531.1791315-2267715-152365080812646/ > /dev/null 2>&1'
changed: [localhost] => 
    changed: true
    cmd: echo '`'
    delta: '0:00:00.002532'
    end: '2026-08-05 14:55:31.352849'
    msg: ''
    rc: 0
    start: '2026-08-05 14:55:31.350317'
    stderr: ''
    stderr_lines: <omitted>
    stdout: '`'
    stdout_lines: <omitted>

TASK [shell] ******************************************************************************************************************************************************************************************************
task path: /playbooks/tests/backtik.yml:12
Wednesday 05 August 2026  14:55:31 +0200 (0:00:00.244)       0:00:00.266 ****** 
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: root
<localhost> EXEC /bin/bash -c 'echo ~root'
<localhost> EXEC /bin/bash -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp `"&& mkdir "` echo /root/.ansible/tmp/ansible-tmp-1785934531.3913958-2267803-49101008822515 `" && echo ansible-tmp-1785934531.3913958-2267803-49101008822515="` echo /root/.ansible/tmp/ansible-tmp-1785934531.3913958-2267803-49101008822515 `" )'
Using module file /opt/ansible/venv/lib/python3.14/site-packages/ansible/modules/command.py
<localhost> PUT /root/.ansible/tmp/ansible-local-2267613heyjogs3/tmpff2_4n12 TO /root/.ansible/tmp/ansible-tmp-1785934531.3913958-2267803-49101008822515/AnsiballZ_command.py
<localhost> EXEC /bin/bash -c 'chmod u+rwx /root/.ansible/tmp/ansible-tmp-1785934531.3913958-2267803-49101008822515/ /root/.ansible/tmp/ansible-tmp-1785934531.3913958-2267803-49101008822515/AnsiballZ_command.py'
<localhost> EXEC /bin/bash -c '/usr/bin/python3 /root/.ansible/tmp/ansible-tmp-1785934531.3913958-2267803-49101008822515/AnsiballZ_command.py'
<localhost> EXEC /bin/bash -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1785934531.3913958-2267803-49101008822515/ > /dev/null 2>&1'
[ERROR]: Task failed: Module failed: The command exited with a non-zero return code.
Origin: /playbooks/tests/backtik.yml:12:11

10                 executable: /bin/bash
11
12         - shell:
             ^ column 11

Traceback (most recent call last):
  File "/root/.ansible/tmp/ansible-tmp-1785934531.3913958-2267803-49101008822515/AnsiballZ_command.py", line 266, in <module>
    _ansiballz_main(
  File "/root/.ansible/tmp/ansible-tmp-1785934531.3913958-2267803-49101008822515/AnsiballZ_command.py", line 260, in _ansiballz_main
    invoke_module(zipped_mod, encoded_params)
  File "/root/.ansible/tmp/ansible-tmp-1785934531.3913958-2267803-49101008822515/AnsiballZ_command.py", line 140, in invoke_module
    _loader.run_module(
  File "/tmp/ansible_ansible.legacy.command_payload_crccm7an/ansible_ansible.legacy.command_payload.zip/ansible/module_utils/_internal/_ansiballz/_loader.py", line 35, in run_module
    _run_module(
  File "/tmp/ansible_ansible.legacy.command_payload_crccm7an/ansible_ansible.legacy.command_payload.zip/ansible/module_utils/_internal/_ansiballz/_loader.py", line 62, in _run_module
    runpy.run_module(mod_name=module_fqn, init_globals=init_globals, run_name='__main__', alter_sys=True)
  File "<frozen runpy>", line 226, in run_module
  File "<frozen runpy>", line 98, in _run_module_code
  File "<frozen runpy>", line 88, in _run_code
  File "/tmp/ansible_ansible.legacy.command_payload_crccm7an/ansible_ansible.legacy.command_payload.zip/ansible/modules/command.py", line 367, in <module>
    main()
  File "/tmp/ansible_ansible.legacy.command_payload_crccm7an/ansible_ansible.legacy.command_payload.zip/ansible/modules/command.py", line 361, in main
    module.fail_json(**r)
Message: The command exited with a non-zero return code.

The above target exception was the direct cause of the following controller exception:

Traceback (most recent call last):
  File "/opt/ansible/venv/lib/python3.14/site-packages/ansible/executor/task_executor.py", line 324, in _execute
    utr.maybe_raise_on_result()
    ~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/opt/ansible/venv/lib/python3.14/site-packages/ansible/_internal/_task.py", line 1263, in maybe_raise_on_result
    raise _captured.AnsibleResultCapturedError(self.exception.event, self)
ansible._internal._errors._captured.AnsibleResultCapturedError: Module failed: The command exited with a non-zero return code.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/opt/ansible/venv/lib/python3.14/site-packages/ansible/executor/task_executor.py", line 327, in _execute
    raise AnsibleTaskError(obj=self._task.get_ds()) from ex
ansible.errors.AnsibleTaskError: Task failed: Module failed: The command exited with a non-zero return code.

fatal: [localhost]: FAILED! => 
    changed: true
    cmd: echo "'`'"
    delta: '0:00:00.002376'
    end: '2026-08-05 14:55:31.536258'
    msg: The command exited with a non-zero return code.
    rc: 2
    start: '2026-08-05 14:55:31.533882'
    stderr: '/bin/bash: -c: line 1: unexpected EOF while looking for matching ``'''
    stderr_lines: <omitted>
    stdout: ''
    stdout_lines: <omitted>

There is no such issue in pure bsh:

$ var_containing_backtick='`'
$ echo "'${var_containing_backtick}'"
'`'

I do not see anywhere in the official documentation something about the fact that the quote filter is mandatory if any accessed variable contains any number of backticks.

Shell variable expansion is completely different from simple text replacement, which is what Jinja templating essentially does.

This is a different case altogether. Backticks are handled before variable expansion in bash.
Your ansible task is generating this:

echo "'`'"

When executed on the command line, that prompts for the rest of the string looking for the closing backtick.

A better approximation in the CLI of what’s happening with your 2nd Ansible task above would be

$ var_containing_backtick='`'
$ eval "echo \"'${var_containing_backtick}'\""

which produces the same error.

…which is all very interesting, but what are you actually trying to do? Maybe we can help get that working…