我在这里要实现的目标如下
- 从 tar 中解压缩代码.gz - 正在工作
- 在未存档的目录中查找文件名 - 工作
- 根据步骤 2 中获取的文件名在代码目录中查找文件名 -失败
- 从源复制文件:(步骤 2) 目标:(步骤3) - 如果我在步骤 3 的模式部分中使用硬编码文件名,则可以工作
下面提到的是我使用的 Ansible 角色:
- name: Unarchive config files to server
unarchive:
src: "{{ config_dir }}/config.tar.gz"
dest: /tmp
list_files: yes
register: tar_path
- name: Find file names in unarchived config files
find:
paths: "{{ tar_path.dest }}"
file_type: file
recurse: yes
register: tmp_file_path
- name: Find file names in code base
find:
paths: /opt
file_type: file
recurse: yes
patterns:
#Search for file names with the values in tmp_file_path
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
command: cp "{{ item.0 }}" "{{ item.1.path }}"
with_together:
- "{{ tmp_file_path.files|map(attribute='path')|list|sort }}"
- "{{ code_files|sort(attribute='name') }}"
在这里,我需要使用 find 根据我在/tmp 中未存档的模式(文件名)在/opt 目录中找到文件
最后,根据文件名和路径将文件从/tmp 替换为/opt(这是我能够做到的)。目录结构如下:
shell> tree tmp
tmp
├── file1
├── file2
└── file3
shell> tree opt
opt
├── bar
│ └── file2
├── baz
│ └── file3
└── foo
└── file1
在这里,如果我使用以下代码,其中我手动提及文件名,那么它可以工作。但是,我不想那样做
- name: Find file names in code base
find:
paths: /opt
file_type: file
recurse: yes
patterns:
- file1
- file2
- file3
register: code_file_path
我需要一个解决方案来替换模式的硬编码:file1、file2 和 file3,并使用一些变量来做到这一点。我需要替换的/tmp 和/opt 中的文件名完全相同
如果我理解正确,这里有一种可能的方法来处理你正在尝试做的事情。在下面的示例中,我取消了取消存档作业,因为它不在关键路径上。
行动手册演练
-
我创建了两个示例目录。前两个任务只是为了进一步向您展示此测试结构:
- 一个
archive
目录,包含随机目录中的 4 个文件。其中一个不在目标中 - 包含多个文件的
code
目录 随机目录。3 个文件与在archive
中找到的其他文件具有相同的基本名称。
- 一个
-
第一个
find
任务与您的任务相同,并注册一个结果,其中包含archive
目录中所有文件的详细信息。 -
对于
code
目录中的第二个find
任务,关键点是将第一次搜索中的基本名称列表作为参数patterns
传递,您可以使用表达式获得该列表:{{ search_archive.files | map(attribute='path') | map('basename') | list }}
我们可以将这个详细说明为:从我们的存档
find
结果中获取files
列表,仅提取path
属性,对每个列表元素应用basename
过滤器并返回一个列表。 -
对于最后一个任务,我使用了
copy
模块。我的示例在本地主机上运行,但由于您的示例可能会在远程目标上运行,因此必须设置remote_src
(否则将从控制器获取文件)。循环是在上一个任务的结果上完成的,因此我们只获取代码目录中的匹配文件作为
dest
。要选择src
,我们使用以下表达式在存档文件夹中查找相应的文件:{{ search_archive.files | map(attribute='path') | select('match', '^.*/' + item | basename + '$') | first }}
选择筛选器
select
将match
测试应用于列表中的每个路径,仅选择以当前代码路径基名称结尾的元素。first
筛选器仅获取第一个(并且仅在您的情况下)匹配元素。loop_control.label
用于获得更好的任务结果输出。
演示手册
前两个任务仅用于调试/演示目的。
---
- name: Update files from package in code wherever they are
hosts: localhost
gather_facts: false
tasks:
- name: Capture sample data structure
command: tree archive code
register: structure
changed_when: false
- name: Show sample data structure
debug:
msg: "{{ structure.stdout_lines}}"
- name: Find files in archive
find:
paths: archive
file_type: file
recurse: yes
register: search_archive
- name: Find files in code matching names in archive
find:
paths: code
file_type: file
recurse: yes
patterns: >-
{{
search_archive.files |
map(attribute='path') |
map('basename') |
list
}}
register: search_code
- name: Copy files from archive to code
vars:
archive_source: >-
{{
search_archive.files |
map(attribute='path') |
select('match', '^.*/' + item | basename + '$') |
first
}}
copy:
remote_src: yes
src: "{{ archive_source }}"
dest: "{{ item }}"
loop: "{{ search_code.files | map(attribute='path') | list }}"
loop_control:
label:
Source: "{{ archive_source }}"
Destination: "{{ item }}"
结果
PLAY [Update files from package in code wherever they are] *****************************************************************************************************************************************************************************
TASK [Capture sample data structure] ***************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Show sample data structure] ******************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
"archive",
"├── a_dir",
"│ └── file2",
"├── file1.txt",
"├── file3",
"└── other_dir",
" └── bla",
" └── fileX",
"code",
"├── dir1",
"│ └── file1.txt",
"├── dir2",
"│ ├── file2",
"│ ├── pipo",
"│ └── toto",
"└── dir3",
" └── subdir",
" └── file3",
"",
"7 directories, 9 files"
]
}
TASK [Find files in archive] ***********************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Find files in code matching names in archive] ************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Copy files from archive to code] *************************************************************************************************************************************************************************************************
changed: [localhost] => (item={'Source': 'archive/file1.txt', 'Destination': 'code/dir1/file1.txt'})
changed: [localhost] => (item={'Source': 'archive/a_dir/file2', 'Destination': 'code/dir2/file2'})
changed: [localhost] => (item={'Source': 'archive/file3', 'Destination': 'code/dir3/subdir/file3'})
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=5 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0