我有一堆目录,其中包含类似的模板文件。
像这样的东西:
/ansible/
└── roles
└── webserver
├── tasks
│ └── main.yml
└── templates
└── sites
├── bar
│ ├── index.html
│ ├── script.js
│ ├── style.css
│ └── webapp.conf
├── baz
│ ├── index.html
│ ├── script.js
│ ├── style.css
│ └── webapp.conf
└── foo
├── index.html
├── script.js
├── style.css
└── webapp.conf
在任务中,我想列出目录(
bar
、baz
、foo
,以及我在那里创建的任何新目录)并将模板操作应用于它们的文件。
我试过使用
with_fileglob
但它不能列出目录(只有文件,它会忽略目录)。
- name: Template index.html
template:
src: "sites/{{item}}/index.html"
dest: "/var/www/{{item}}/index.html"
with_fileglob: "templates/sites/*"
还有一个回归以至于它甚至无法在目录中找到带有glob的文件。
- name: Template index.html
template:
src: "sites/{{item}}"
dest: "/var/www/{{item}}/index.html"
with_fileglob: "templates/sites/*/index.html"
我试过了
find
,但我无法让它在本地模板目录上工作(它似乎在主机上找到了东西。)
我试过了
filetree
- name: Template index.html
template:
src: "sites/{{item.path}}"
dest: "/var/www/{{item.path}}/index.html"
with_filetree: "templates/sites/"
when: item.state == 'directory'
但是它:
when
将其限制为目录,它返回子目录)有没有办法在我的本地模板目录中获取我所有站点的列表?
如果你想使用
filetree
并使其不那么冗长并满足你的需要,你可以将它用作查找,而不是作为 with_*
循环。
selectattr
和rejectattr
过滤器的帮助下进行预过滤:
例如:
- debug:
msg: "{{ item.path }}"
loop: >-
{{
lookup('community.general.filetree', 'templates/sites')
| selectattr('state', '==', 'directory')
| rejectattr('path', 'contains', '/')
}}
loop_control:
label: "{{ item.path }}"
在这个任务中:
selectattr('state', '==', 'directory')
将确保您只列出目录rejectattr('path', 'contains', '/')
将拒绝包含 /
的路径,因此有效地排除了子目录loop_control
及其参数 label: "{{ item.path }}"
将使复杂词典上的循环不那么冗长或者您可以使用 JMESPath,因为您确实部分使用了它,但过滤可以是查询的一部分:
- debug:
msg: "{{ item }}"
loop: >-
{{
lookup('community.general.filetree', 'templates/sites')
| json_query('[?state == `directory` && !contains(path, `/`)].path')
}}
鉴于文件结构:
.
└── templates
└── sites
├── bar
│ ├── baz
│ └── index.html
└── foo
└── index.html
baz
是一个子目录,上面的两个任务都会产生:
ok: [localhost] => (item=bar) =>
msg: bar
ok: [localhost] => (item=foo) =>
msg: foo
简而言之,使用您之前引用的
find
模块
在
roles/webserver/vars/main.yml
---
available_sites: "{{ find_sites_subdirs.files | d([])
| map(attribute='path') | map('basename') }}"
注意:
d
是 default
的别名,只是为了避免在运行下面的 find
之前使用 var 时出现错误(在这种情况下它将是一个空列表)
然后在
roles/webserver/tasks/main.yml
---
- name: Find sites subdirs
ansible.builtin.find:
paths:
- "{{ role_path }}/templates/sites"
file_type: directory
recurse: false
register: find_sites_subdirs
delegate_to: localhost
run_once: true
- name: Template index.html
ansible.builtin.template:
src: "sites/{{ item }}/index.html"
dest: "/var/www/{{ item }}/index.html"
loop: "{{ available_sites }}"
我找到了一种使用
filetree
和一些管道的方法,可以将其过滤到基本目录。
- name: Template index.html
template:
src: "sites/{{item}}"
dest: "/var/www/{{item}}/index.html"
with: >-
{{
lookup('filetree', 'templates/sites')
| selectattr('state', 'eq', 'directory')
| rejectattr('path', 'contains', '/')
| map(attribute='path')
| list
}}
lookup('filetree', 'templates/sites')
做目录的递归扫描selectattr('state', 'eq', 'directory')
过滤掉文件rejectattr('path', 'contains', '/')
过滤掉子目录map(attribute='path')
仅返回 item.path
的列表,而不是包含大量无关字段的完整对象。list
将其转换为可与 with:
要多次使用这个列表,把它放在一个变量中效率会更高。它可以像这样进入
roles/webserver/vars/main.yml
:
my_sites: >-
{{
lookup('filetree', 'templates/sites')
| selectattr('state', 'eq', 'directory')
| rejectattr('path', 'contains', '/')
| map(attribute='path')
| list
}}
然后模板就可以使用
with: "{{my_sites}}"