用例
- 列出目录中的所有文件,格式为 -
a1.{{ env }}.js
,a2.{{ env }}.js
- 在目标目录中查找格式为 -
a1.js
、a2.js
的相应文件 - 将
a1.{{ env }}.js
复制到a1.js
所在的目录中,将a2.{{ env }}.js
复制到a2.js
所在的目录中
示例代码:此代码执行直接查找和替换
- name: Find files in archive
find:
paths: "archive/"
file_type: file
recurse: yes
register: tmp_file_path
- name: Find files in code matching names in archive
find:
paths: "code/"
file_type: file
recurse: yes
patterns: "{{ tmp_file_path.files | map(attribute='path') | map('basename') | list }}"
register: code_file_path
- set_fact:
code_files: "{{ code_files|default([]) +
[{'path': item, 'name': item|basename}] }}"
loop: "{{ code_file_path.files|map(attribute='path')|list }}"
- name: Copy files from archive to code directory
command: cp "{{ item.0 }}" "{{ item.1.path }}"
when:
- item.0|basename == item.1.path|basename
with_together:
- "{{ tmp_file_path.files|map(attribute='path')|list|sort }}"
- "{{ code_files|sort(attribute='name') }}"
下面列出的是目录结构
├── archive
│ ├── a1.test.js
│ ├── a2.test.js
│ ├── a3.test.js
│ └── a4.test.js
└── code
├── a1.js
├── dir1
│ └── a2.js
└── dir2
├── a4.js
└── dir3
└── a3.js
- 将
archive/a1.test.js
复制到code/
- 将
archive/a2.test.js
复制到code/dir1/
- 将
archive/a3.test.js
复制到code/dir1/dir2/dir3/
- 将
archive/a4.test.js
复制到code/dir1/dir2/
是否有解决方案可以根据上述用例进行直接复制?
关于该方法的一些解释:
-
它的整个想法是基于创建一个字典,指示剧本应该在哪个位置添加文件.
字典,对于您的用例,如下所示:{ "a1.js": { "archive": "archive/a1.test.js", "paths": [ "code" ] }, "a2.js": { "archive": "archive/a2.test.js", "paths": [ "code/dir1" ] }, "a3.js": { "archive": "archive/a3.test.js", "paths": [ "code/dir1/dir2/dir3" ] }, "a4.js": { "archive": "archive/a4.test.js", "paths": [ "code/dir1/dir2" ] } }
其中键是我们在
code
文件夹下搜索的文件,键archive
表示我们要从archive
文件夹中复制的文件,paths
是一个数组,所述文件应该在其中找到其目的地。 -
大多数逻辑是由 Ansible 过滤器
regex_replace
完成的,它通过一个非常简单的表达式提取要在code
文件夹中查找的文件的名称:(.*)..*.js$
item.path | basename | regex_replace('(.*)\..*\.js$', '\1.js')
-
这里使用的另一个过滤器可能会很有趣,是
combine
过滤器,带有参数recursive=true
,它允许创建文件应在其中找到其目的地的paths
。 -
这里还用了一个Python操作:
dict.keys()
,为了创建逗号分隔的文件列表,以便在code
文件夹中搜索上面的字典的键。 -
它还利用
subelement
过滤器的loop
,同时遍历字典及其子数组paths
。 -
为了完整起见,以下是本剧本中使用的其他更常用的筛选器:
default
:在未定义变量时指定默认值basename
:仅从其完整路径中获取文件名dirname
:仅从文件的完整路径获取目录join
:在这里,基于分隔符连接数组的元素
-
是的,与您的用例相比,它可能被过度设计,下面的剧本能够应对
a1.js
可能位于两个不同文件夹中的事实,并且能够在这两个文件夹中复制a1.test.js
。
因此,这是一个解决方案:
- hosts: localhost
gather_facts: no
tasks:
- find:
paths: archive
file_type: file
recurse: yes
register: archives
- set_fact:
searches: "{{ searches | default({}) | combine({ key: value }) }}"
vars:
key: "{{ item.path | basename | regex_replace('(.*)\..*\.js$', '\1.js') }}"
value: "{{ { 'archive': item.path, 'paths': [] } }}"
loop: "{{ archives.files }}"
loop_control:
label: "{{ item.path }}"
- find:
path: code
file_type: file
recurse: yes
pattern: "{{ searches.keys() | join(',') }}"
register: paths
- set_fact:
searches: "{{ searches | combine({key: value}, recursive=true) }}"
vars:
key: "{{ item.path | basename }}"
value: "{{ { 'paths': [item.path | dirname] + searches[item.path | basename].paths } }}"
loop: "{{ paths.files }}"
loop_control:
label: "{{ item.path }}"
- copy:
src: "{{ item.0.archive }}"
dest: "{{ item.1 ~ '/' ~ item.0.archive | basename }}"
loop: "{{ searches | subelements('paths') }}"
loop_control:
label: "{{ item.0.archive }}"
之前的情况:
tree archive code
archive
├── a1.test.js
├── a2.test.js
├── a3.test.js
└── a4.test.js
code
├── a1.js
└── dir1
├── a2.js
└── dir2
├── a4.js
└── dir3
└── a3.js
3 directories, 8 files
剧本回顾:
PLAY [localhost] **************************************************************************************************
TASK [find] *******************************************************************************************************
ok: [localhost]
TASK [set_fact] ***************************************************************************************************
ok: [localhost] => (item=archive/a3.test.js)
ok: [localhost] => (item=archive/a2.test.js)
ok: [localhost] => (item=archive/a1.test.js)
ok: [localhost] => (item=archive/a4.test.js)
TASK [find] *******************************************************************************************************
ok: [localhost]
TASK [set_fact] ***************************************************************************************************
ok: [localhost] => (item=code/a1.js)
ok: [localhost] => (item=code/dir1/a2.js)
ok: [localhost] => (item=code/dir1/dir2/a4.js)
ok: [localhost] => (item=code/dir1/dir2/dir3/a3.js)
TASK [copy] *******************************************************************************************************
changed: [localhost] => (item=archive/a3.test.js)
changed: [localhost] => (item=archive/a2.test.js)
changed: [localhost] => (item=archive/a1.test.js)
changed: [localhost] => (item=archive/a4.test.js)
PLAY RECAP ********************************************************************************************************
localhost : ok=5 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
之后的情况:
tree code
code
├── a1.js
├── a1.test.js
└── dir1
├── a2.js
├── a2.test.js
└── dir2
├── a4.js
├── a4.test.js
└── dir3
├── a3.js
└── a3.test.js
3 directories, 8 files