在Ansible中如何转义双花括号

115

如何在Ansible 1.9.2中避免双大括号?

例如,在以下shell命令中,如何转义双大括号?

- name: Test 
  shell: "docker inspect --format '{{ .NetworkSettings.IPAddress }}' instance1"

1
注意:这里的问题在于 inspect docker 客户端命令输入一个使用双大括号作为 jinja2 模板的 go 模板。我需要一种方法可以将大括号传递到目标服务器上运行的最终 Ansile 命令中。请参阅 http://docs.docker.com/reference/commandline/inspect。 - Davide Guerri
10个回答

172

如果在Ansible中存在字符冲突问题,一个经验法则是将它们作为Jinja表达式中的字符串输出。

因此,您可以使用{{ '{{' }}代替{{

- debug: msg="docker inspect --format '{{ '{{' }} .NetworkSettings.IPAddress {{ '}}' }}' instance1"

Jinja2文档中的“转义”主题。


1
我无法让它正常工作。我开始觉得我漏了什么... 我得到了这个: changed: [docker-server.local] => {"changed": true, "cmd": "docker inspect --format '{# .NetworkSettings.IPAddress #}' glance-api.os-in-a-box", "delta": "0:00:00.029344", "end": "2015-08-29 17:36:37.962143", "rc": 0, "start": "2015-08-29 17:36:37.932799", "stderr": "", "stdout": "172.17.1.6", "warnings": []} - Davide Guerri
不需要闭合大括号 - Nelson G.
1
这个不起作用。我不知道为什么会有这么多赞 - 这只适用于一个级别。如果你创建一个变量 raw_var: '{{ '{{' }}something{{ '}}' }}' 并在其他地方引用它 command: "{{ raw_var }}",你会得到 Error: something is undefined - Megakoresh
3
raw_var: "{{ '{{' }}something{{ '}}' }}" 的翻译为:raw_var: "{{ '{{' }}something{{ '}}' }}" - udondan
你可以将整个字符串包装在{{''}}中,使其更易读。因此,要转义{{.ImageName}}|{{.Name}}|{{.ImageFullID}}|{{.FullID}} 您需要编写{{'{{.ImageName}}|{{.Name}}|{{.ImageFullID}}|{{.FullID}}'}}。 - Niwla23

78

这个:

- name: Test 
  shell: "docker inspect --format {% raw %}'{{ .NetworkSettings.IPAddress }}' {% endraw %} instance1"

应该有效

另一种方法是使用反斜杠,例如\{\{ .NetworkSettings.IPAddress \}\}

希望能帮到你


2
谢谢Filippe,但我已经尝试过两种方法,它们都不起作用 :( - Davide Guerri
7
我刚在1.9.2中测试了一下,{ raw }...{% endraw %}对我有效。但是转义确实不起作用。它会导致\\{\\{ .NetworkSettings.IPAddress \\}\\}的结果。 - udondan
看起来它可以工作,但不幸的是,Ansible 尝试展开{{ .NetworkSettings.IPAddress }}。这是我得到的: changed: [docker-server.local] => {"changed": true, "cmd": "docker inspect --format '{# .NetworkSettings.IPAddress #}' glance-api.os-in-a-box", "delta": "0:00:00.028659", "end": "2015-08-29 17:25:53.416944", "rc": 0, "start": "2015-08-29 17:25:53.388285", "stderr": "", "stdout": "172.17.1.6", "warnings": []} - Davide Guerri
1
而且,作为额外的奖励,甚至无法调试上一步的输出!:“致命错误:[docker-server.local] => 无法模板化{{test}}:无法模板化docker inspect --format '{{ .NetworkSettings.IPAddress }}' glance-api.os-in-a-box:在模板化字符串时出现模板错误:意外的'.'” - Davide Guerri
1
可以使用以下技巧进行调试:
  • name: 查找某个主机的IPv6地址 shell: docker inspect -f "{% raw %} {{ .NetworkSettings.GlobalIPv6Address }} {% endraw %}" {{some.host}} > tmp.txt
  • command: cat tmp.txt register: result
  • debug: msg="{{ result.stdout }}"
- lanwen
显示剩余6条评论

29

使用ansible 2.1.1.0尝试过

{%raw%}...{%endraw%} 块似乎是更清晰的方式

- name: list container images and name date on the server
  shell: docker ps --format {%raw%}"{{.Image}} {{.Names}}"{%endraw%}

只需要转义开头的 '{{' 符号

tasks:
- name: list container images and names
  shell: docker ps --format "{{'{{'}}.Image}} {{'{{'}}.Names}}"

除了阅读更困难之外,没有什么危害可以逃脱尾随的 '}}'。

tasks:
- name: list container images and names
  shell: docker ps --format "{{'{{'}}.Image{{'}}'}} {{'{{'}}.Names{{'}}'}}"

反斜杠 '\' 似乎不起作用


15

Ansible 2.0新增了使用!unsafe标记将某个值声明为不安全的能力。

在你的示例中,你可以这样做:

- name: Test 
  shell: !unsafe "docker inspect --format '{{ .NetworkSettings.IPAddress }}' instance1"

请查看文档以获取详细信息。


1
为什么是2.5?……2.0 - Konstantin Suvorov
3
嘘,我说的是2.0版本。 - Jess
它适用于2.4.6版本,我在文档中找到了解决方案,所以我可以确认。 - eco
交换单引号和双引号看起来更像 Linux 的方式!unsafe "docker inspect --format '{{ .NetworkSettings.IPAddress }}' instance1" - Timmy Chiu
个人而言,我喜欢将所有Jinja模板放在双引号中,以提示任何阅读代码的人。大多数具有字符串插值的语言将单引号视为字面量,将双引号视为可模板化的内容,我尝试在ansible中复制这种方式(即使不是必需的)。此外,使用单引号将shell脚本中的参数包装起来可以减少可以解释为“'”和“\”的字符数量,如果变量中有“$”,可能会避免出现问题。无论哪种引用方式都可以工作。 - Jess

6

我有一个类似的问题:我需要发布一个JSON文档,该文档由一个包含一些go模板变量(是的,我知道:-P)的jinja2模板生成,例如

"NAME_TEMPLATE": %{{service_name}}.%{{stack_name}}.%{{environment_name}}

尝试将模板的此部分围栏在

{% raw %} ... {% endraw %}

之间没有起作用,因为ansible中有一些神奇的东西会运行模板和变量替换两次(我不确定这点,但它看起来绝对像这样)

当尝试使用该模板时,您最终会得到“未定义的变量service_name”...

所以我最终使用了!unsafe{% raw %} ... {% endraw %}的组合来定义一个事实,该事实后来在模板中使用。

- set_fact:
   __rancher_init_root_domain: "{{ rancher_root_domain }}"
   #!unsafe: try to trick ansible into not doing substitutions in that string, then use %raw% so the value won't substituted another time
   __rancher_init_name_template: !unsafe "{%raw%}%{{service_name}}.%{{stack_name}}.%{{environment_name}}{%endraw%}"

- name: build a template for a project
  set_fact:
    __rancher_init_template_doc: "{{ lookup('template', 'templates/project_template.json.j2') }}"

模板包含以下内容:
    "ROOT_DOMAIN":"{{__rancher_init_root_domain}}",
    "ROUTE53_ZONE_ID":"",
    "NAME_TEMPLATE":"{{__rancher_init_name_template }}",
    "HEALTH_CHECK":"10000",

输出结果正常:

"NAME_TEMPLATE": "%{{service_name}}.%{{stack_name}}.%{{environment_name}}",

非常不幸,但似乎有时Ansible会做一些奇怪的事情,这种双重转义结构是处理它的唯一方法... - WindyFields

6

这里有一个比 udondan 的答案 更简短的替代方案; 将整个字符串用双括号括起来:

shell: "docker inspect --format {{ '{{ .NetworkSettings.IPAddress }}' }} instance1"

4

已经提到过使用raw的解决方案,但是很可惜之前答案中的命令对我来说不起作用。

没有ansible:

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' docker_instance_name

使用Ansible:

- name: Get ip of db container
  shell: "{% raw %}docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' docker_instance_name{% endraw %}"
  register: db_ip_addr
- debug:
  var: db_ip_addr.stdout

2
我使用了一个小脚本解决了我的问题:

#!/usr/bin/env bash

docker inspect --format '{{ .NetworkSettings.IPAddress }}' "$1"

以下是 Ansible play 的代码:

- copy:
    src: files/get_docker_ip.sh
    dest: /usr/local/bin/get_docker_ip.sh
    owner: root
    group: root
    mode: 0770

- shell: "/usr/local/bin/get_docker_ip.sh {{ SWIFT_ACCOUNT_HOSTNAME }}"
  register: swift_account_info

然而,令人惊讶的是Ansible不允许转义双大括号!


1

我无法让@Ben的答案工作(shell:!unsafe ...

以下是完整且可行的答案,针对Ansible >2.0进行了更新,以回答OP的问题

---
# file: play.yml

- hosts: localhost
  connection: local
  gather_facts: no
  vars:
    # regarding !unsafe, please see:
    # https://docs.ansible.com/ansible/latest/user_guide/playbooks_advanced_syntax.html
    #
    - NetworkSettings_IPAddress: !unsafe "{{.NetworkSettings.IPAddress}}"
  tasks:
    - shell: "docker inspect --format '{{NetworkSettings_IPAddress}}' instance1"
      register: out
    - debug: var="{{item}}"                                                                                                   
      with_items:                                                                                                             
        - out.cmd                                                                                                             
        - out.stdout                                                                                                          

输出:([警告]已删除)

# ansible-playbook play.yml
PLAY [localhost] ***************************************************************

TASK [shell] *******************************************************************
changed: [localhost]

TASK [debug] *******************************************************************
ok: [localhost] => (item=out.cmd) => {
    "item": "out.cmd", 
    "out.cmd": "docker inspect --format '{{.NetworkSettings.IPAddress}}' instance1"
}
ok: [localhost] => (item=out.stdout) => {
    "item": "out.stdout", 
    "out.stdout": "172.17.0.2"
}

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

# ansible --version | head -1
ansible 2.6.1

-8
这里是一个大部分干净并且使用花括号不依赖于docker --inspect的Ansible原生解决方法。我们假设之前刚引用了一个容器,使用了Ansible docker module
- name: query IP of client container
  shell: "docker exec {{ docker_containers[0].Id }} hostname -I"
  register: _container_query

- name: get IP of query result
  set_fact:
    _container_ip: "{{ _container_query.stdout | regex_replace('\\s','') }}"

现在你已经在变量_container_ip中拥有了Docker容器的IP。我还在我的文章 Ansible与Docker的结合中发布了这个解决方法。

[更新2015-11-03] 删除了容器查询的标准输出的空格。

[更新2015-11-04] 顺便说一下,官方Ansible存储库中有两个拉取请求,通过恢复Docker模块返回的事实,这个解决方法就不再需要了。所以你可以通过docker_containers[0].NetworkSettings.IPAddress访问Docker容器的IP。请投票支持这些拉取请求:


非常好。这个答案在当前的Docker实现中可以直接使用。在我的情况下,我需要添加一些处理,因为我正在为某些容器添加额外的接口。尽管如此,这种方法非常简洁和干净。谢谢! - Davide Guerri
为了完整起见,在某些容器中我正在添加多个接口:虚拟桥和veth。 我所说的项目在这里:https://github.com/dguerri/dockerstack - Davide Guerri
10
这并没有回答“如何在Ansible 1.9.2中转义双大括号”的问题。 - augurar
3
因为这个回答对99%的使用情况完全没有帮助,所以被踩了。可行的回答是:通过jinja tpl进行花括号打印:https://dev59.com/iVwY5IYBdhLWcg3wup1c#32283447 - Jay Taylor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接