Search code examples
ansibleyamljinja2

Replacing / by \ in a string with ansible


I need to replace all the / by \ in a string stored in a variable.

I'm just trying to do it a simple as possible to test it with a debug, but no matter how I try it I dont get the expected result of just replacing character to character. I think it's probably just a single/double quote problem or maybe the \ needs to be escaped in a certain way I don't know.

vars:
    - SecGroup: '/stuff/foo/thing'
tasks:
    - name: Display modified var
      debug:
        msg: "{{ SecGroup | replace('/','\') }}"

Expected output : \stuff\foo\thing

Output with differents tries :

- name: Display modified var
      debug:
        msg: "{{ SecGroup | replace('/','\') }}"

TASK [Display modified var] 
ok: [localhost] => {
    "msg": "stufffoothing"
}


- name: Display modified var
      debug:
        msg: "{{ SecGroup | replace('/','\\') }}"

TASK [Display modified var] 
fatal: [localhost]: FAILED! => {"msg": "Unexpected failure during module execution."}


- name: Display modified var
      debug:
        msg: "{{ SecGroup | replace('/','\\\') }}"

TASK [Display modified var] 
fatal: [localhost]: FAILED! => {"msg": "Unexpected failure during module execution."}


- name: Display modified var
      debug:
        msg: "{{ SecGroup | replace('/','\\\\') }}"

TASK [Display modified var] 
ok: [localhost] => {
    "msg": "\\\\stuff\\\\foo\\\\thing"
}

I also tried to revert the quotes :

- name: Display modified var
      debug:
        msg: '{{ SecGroup | replace("/","\") }}'

TASK [Display modified var]
fatal: [localhost]: FAILED! => {"msg": "Unexpected failure during module execution."}

I can't explain the output of this one

- name: Display modified var
      debug:
        msg: '{{ SecGroup | replace("/","\\") }}'

TASK [Display modified var] 
ok: [localhost] => {
    "msg": "\\\\stuff\\\\foo\\\\thing"
}

Solution

  • I think you've stumbled upon an edge case that involves the interaction between YAML escaping and Python escaping. The only way I was able to get it to work was introducing a guard character -- something to ensure that the \ isn't the last character in the expression, which we then remove with a subsequent replace() filter. Here I'm using a semicolon (;), but you could use anything that you're certain won't be in your SecGroup string. Note that your choice of quotes is significant; quoting the entire string with single quotes inhibits YAML escaping:

    - name: With guard character
      debug:
        msg: '{{ SecGroup | replace("/","\;") | replace(";", "") }}'
    

    Outputs:

    TASK [With guard character] *******************************************************************************************************************************************************************
    ok: [localhost] => {
        "msg": "\\stuff\\foo\\thing"
    }
    

    Which is exactly what you want (remembering that a single \ is encoded as \\ in JSON output).


    Regarding this:

    - name: Display modified var
          debug:
            msg: '{{ SecGroup | replace("/","\\") }}'
    
    TASK [Display modified var] 
    ok: [localhost] => {
        "msg": "\\\\stuff\\\\foo\\\\thing"
    }
    

    You are successfully replacing / with two backslashes, \\. Since a backslash must be encoded as \\ in JSON output, a double backslash will end up represented as \\\\, so this:

    "msg": "\\\\stuff\\\\foo\\\\thing"
    

    Means you actually have the string:

    \\stuff\\foo\\thing