S_lion's Studio

(十一)Jinja2模板

字数统计: 998阅读时长: 4 min
2021/12/02 Share

日常ansible playbook编写过程中,会涉及到很多变量的解析与替换,解析并替换模板表达式的过程称为渲染。

ansibles使用Jinja2来完成渲染工作,它是Python的一种模板引擎。

Jinja2模板引擎提供了三种特殊符号来包围模板表达式:

  1. {{xxx}}:双大括号包围变量或表达式(Ansible中的变量就是它包围的)
  2. {#xxx#}:Jinja2的注释符号
  3. {%xxx%}:Jinja2的一些特殊关键字标签,比如if语句、for循环语句等等

Jinja2条件判断

Jinja2中可以使用if语句进行条件判断。

语法如下:

1
2
3
4
5
6
7
8
9
{% if CONDITION1 %}
string_or_expression1
{% elif CONDITION2 %}
string_or_expression2
{% elif CONDITION3 %}
string_or_expression3
{% else %}
string_or_expression4
{% endif %}

其中elif和else分支都是可省略的。CONDITION部分是条件表达式

例如:

当前结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@slions_pc1 ansible_poc]# tree
.
├── test.yml
├── ansible.cfg
├── ansible.log
├── facts
│   ├── 192.168.100.10
│   └── 192.168.100.11
├── inventory
│   └── hosts
└── roles
└── first
├── tasks
│   └── main.yml
└── templates
└── my.j2

其中模板文件my.j2内容是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{% if whatday == "0" %}
周日
{% elif whatday == "1" %}
周一
{% elif whatday == "2" %}
周二
{% elif whatday == "3" %}
周三
{% elif whatday == "4" %}
周四
{% elif whatday == "5" %}
周五
{% elif whatday == "6" %}
周六
{% else %}
输入错误
{% endif %}

上面判断变量whatday的值,然后输出对应的星期几。因为whatday变量的值是字符串,所以让它和字符串形式的数值进行等值比较。当然,也可以使用筛选器将字符串转换为数值后进行数值比较:whatday|int == 0。

roles/first/tasks中的main.yml内容为:

1
2
3
4
---
- template:
src: my.j2
dest: /tmp/dayfile

任务入口文件test.yml内容为:

1
2
3
4
5
6
7
8
9
- hosts: localhost
gather_facts: false
vars_prompt:
- name: whatday
default: 0
prompt: "输入今天星期几(0:星期日,1:星期一,...,6:星期六)"
privat: no
roles:
- first

执行后可以在/tmp/dayfile中查看到替换后的对应字符。

如果if语句的分支比较简单(没有elif逻辑),那么可以使用行内if表达式。

其语法格式为:

1
string_or_expr1 if CONDITION else string_or_expr2

因为行内if是表达式而不是语句块,所以不使用 {%%} 符号,而使用{{}} 。

例如:

1
2
- debug:
msg: "{{'周末' if whatday|int > 5 else '工作日'}}"

for循环迭代

for迭代列表

for循环的语法:

1
2
3
{% for i in LIST %}
string_or_expression
{% endfor %}

还支持直接条件判断筛选要参与迭代的元素:

1
2
3
{% for i in LIST if CONDITION %}
string_or_expression
{% endfor %}

此外,Jinja2的for语句还允许使用else分支,如果for所迭代的列表LIST是空列表(或没有元素可迭代),则会执行else分支。

1
2
3
4
5
{% for i in LIST %}
string_or_expression
{% else %}
string_or_expression
{% endfor %}

例如,在模板文件a.txt.j2中有如下内容:

1
2
3
4
5
{% for file in files %}
<{{file}}>
{% else %}
no file in files
{% endfor %}

playbook文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
---
- hosts: localhost
gather_facts: no
tasks:
- template:
src: a.txt.j2
dest: /tmp/a.txt
vars:
files:
- /tmp/a1
- /tmp/a2
- /tmp/a3

执行playbook之后,将生成包含如下内容的/tmp/a.txt文件:

1
2
3
</tmp/a1>
</tmp/a2>
</tmp/a3>

如果将playbook中的files变量设置为空列表,则会执行else分支,所以生成的/tmp/a.txt的内容为:

1
no file in files

如果files变量未定义或变量类型不是list,则默认会报错。针对未定义变量,可采用如下策略提供默认空列表:

1
2
3
4
5
{% for file in (files|default([])) %}
<{{file}}>
{% else %}
no file in files
{% endfor %}

如果不想迭代文件列表中的/tmp/a3,则可以加上条件判断:

1
2
3
4
5
{% for file in (files|default([])) if file != "/tmp/a3" %}
<{{file}}>
{% else %}
no file in files
{% endfor %}

Jinja2的for循环没有提供break和continue的功能,所以只能通过{% for…if…%}来间接实现类似功能。

CATALOG
  1. 1. Jinja2条件判断
  2. 2. for循环迭代
    1. 2.1. for迭代列表