Ansible - 查找和替换



用例

  1. 列出目录中的所有文件,格式为 -a1.{{ env }}.jsa2.{{ env }}.js
  2. 在目标目录中查找格式为 -a1.jsa2.js的相应文件
  3. 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/

是否有解决方案可以根据上述用例进行直接复制?

关于该方法的一些解释:

  1. 它的整个想法是基于创建一个字典,指示剧本应该在哪个位置添加文件.
    字典,对于您的用例,如下所示:

    {
    "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是一个数组,所述文件应该在其中找到其目的地。

  2. 大多数逻辑是由 Ansible 过滤器regex_replace完成的,它通过一个非常简单的表达式提取要在code文件夹中查找的文件的名称:(.*)..*.js$

    item.path | basename | regex_replace('(.*)\..*\.js$', '\1.js')
    
  3. 这里使用的另一个过滤器可能会很有趣,是combine过滤器,带有参数recursive=true,它允许创建文件应在其中找到其目的地的paths

  4. 这里还用了一个Python操作:dict.keys(),为了创建逗号分隔的文件列表,以便在code文件夹中搜索上面的字典的键。

  5. 它还利用subelement过滤器的loop,同时遍历字典及其子数组paths

  6. 为了完整起见,以下是本剧本中使用的其他更常用的筛选器:

    • default:在未定义变量时指定默认值
    • basename:仅从其完整路径中获取文件名
    • dirname:仅从文件的完整路径获取目录
    • join:在这里,基于分隔符连接数组的元素
  7. 是的,与您的用例相比,它可能被过度设计,下面的剧本能够应对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

最新更新