Ansible:用一个命令将多个文件从多个源复制到多个目标

Ansible:用一个命令将多个文件从多个源复制到多个目标,ansible,Ansible,我正在尝试将一些配置文件从/tmp复制到/opt。 首先,我递归搜索/tmp和/opt目录中的文件,并将其分别存储在变量tmp_file_path和code_file_path中,该变量有一个属性files.path,我需要在源和目标中使用该属性进行复制 - name: Find files in tmp find: paths: /tmp/ file_type: file recurse: yes patterns: - file1

我正在尝试将一些配置文件从/tmp复制到/opt。 首先,我递归搜索/tmp和/opt目录中的文件,并将其分别存储在变量tmp_file_path和code_file_path中,该变量有一个属性files.path,我需要在源和目标中使用该属性进行复制

- name: Find files in tmp
  find:
    paths: /tmp/
    file_type: file
    recurse: yes
    patterns:
      - file1
      - file2
      - file3
  register: tmp_file_path
- debug:
    var: tmp_file_path

- name: Find files in code
  find:
    paths: /opt/
    file_type: file
    recurse: yes
    patterns: 
      - file1
      - file2
      - file3
  register: code_file_path
- debug:
    var: code_file_path
这里的源文件路径可以是/tmp/folder1/file1、/tmp/folder2/file2、/tmp/folder13/file3。 目的地可以是/opt/folderA/file1、/opt/folderB/file3、/opt/folderC/file3

到目前为止,我已设法将任务写如下

- name: Copy files from tmp to code directory
  copy:
    src: "{{item.path}}"
    dest: "{{item.path}}"
    remote_src: yes
  with_items:
    - { "{{ tmp_file_path.files }}", "{{ code_file_path.files }}" }
复制必须在单个命令中完成,这样我就不会最终硬编码源和目标的路径。谁能帮我实现这个目标

下面是一段用于将文件从代码文件路径递归复制到/tmp的代码

- name: Copy files from code directory to tmp
  copy:
    src: "{{item.path}}"
    dest: /tmp/
    remote_src: yes
  with_items: "{{code_file_path.files}}"
试试这个

- name: Copy files from tmp to code directory
  copy:
    src: "{{ item.0 }}"
    dest: "{{ item.1 }}"
    remote_src: yes
  with_together:
    - "{{ tmp_file_path.files|map(attribute='path')|list|sort }}"
    - "{{ code_file_path.files|map(attribute='path')|list|sort }}"
首先尝试调试

    - debug:
        msg:
          - "src: {{ item.0 }}"
          - "dest: {{ item.1 }}"
      with_together:
        - "{{ tmp_file_path.files|map(attribute='path')|list|sort }}"
        - "{{ code_file_path.files|map(attribute='path')|list|sort }}"
首先测试是否正常可能很有用

    - debug:
        msg: The numbers of files do not match
      when: tmp_file_path.files|map(attribute='path')|list|length !=
            code_file_path.files|map(attribute='path')|list|length

问:“这没有按预期工作,因为我在code_file_path中搜索的文件有多个子目录。它在code_file_path.files返回的整个文件路径上排序,而不仅仅是文件名。”

答:创建一个包含路径和名称的列表。然后按名称对列表进行排序。比如说

    - set_fact:
        code_files: "{{ code_files|default([]) +
                        [{'path': item, 'name': item|basename}] }}"
      loop: "{{ code_file_path.files|map(attribute='path')|list }}"

    - debug:
        msg:
          - "src: {{ item.0 }}"
          - "dest: {{ item.1.path }}"
      with_together:
        - "{{ tmp_file_path.files|map(attribute='path')|list|sort }}"
        - "{{ code_files|sort(attribute='name') }}"

范例

shell> tree tmp
tmp
├── file1
├── file2
└── file3

shell> tree opt
opt
├── bar
│   └── file2
├── baz
│   └── file3
└── foo
    └── file1
下面的任务

    - set_fact:
        code_files: "{{ code_files|default([]) +
                        [{'path': item, 'name': item|basename}] }}"
      loop: "{{ code_file_path.files|map(attribute='path')|list }}"

    - debug:
        var: code_files|sort(attribute='name')

    - debug:
        msg:
          - "src: {{ item.0 }}"
          - "dest: {{ item.1.path }}"
      with_together:
        - "{{ tmp_file_path.files|map(attribute='path')|list|sort }}"
        - "{{ code_files|sort(attribute='name') }}"
给予


您可以通过以下条件实现这一点:每个文件名都是唯一的,如果/opt目录中的列表中有两个文件具有相同的名称,那么它们的内容应该是相同的

如果是这种情况,则可以使用with_嵌套循环,并在打开文件名时使用conditional

例如:

- name: Copy files from tmp to code directory
  copy:
    src: "{{ item.0 }}"
    dest: "{{ item.1 }}"
    remote_src: yes
  when: 
    - item.0|basename == item.1|basename
  with_nested:
    - "{{ tmp_file_path.files|map(attribute='path')|list }}"
    - "{{ code_file_path.files|map(attribute='path')|list }}"
这个解决方案唯一的“问题”是你会运行循环很多次

您可能还希望将循环syntax与_syntax一起使用,并使用一些循环控制来选择打印的内容:


如果加载了jinja.ext.do扩展名(在ansible.cfg:
jinja2_extensions=jinja2.ext.do
),则可以使用路径构建dict:

- name: Make a dict
  set_fact:
    files_dict: |
      {%- set out_dict = dict() -%}
      {%- for tmp_file in tmp_file_path.files|map(attribute='path')|list -%}
      {%-   do out_dict.update({tmp_file|basename: {'tmp': tmp_file}}) -%}
      {%- endfor -%}
      {%- for opt_file in code_file_path.files|map(attribute='path')|list -%}
      {%-   do out_dict[opt_file|basename].update({'opt': opt_file}) -%}
      {%- endfor -%}
      {{ out_dict }}

- name: Copy from dict
  copy:
    src: "{{ item.value.tmp }}"
    dest: "{{ item.value.opt }}"
    remote_src: yes
  with_dict: "{{ files_dict }}"

@VladimirBotka好的,让我更正一下,我需要将/opt中的文件名file1、file2和file3替换为/opt中的文件/tmp@VladimirBotka是的,你是对的,文件名完全相同。在这里,我可以执行命令来替换文件,但我需要遍历已注册的变量tmp_file_path和code_file_path谢谢您的回答。这没有像预期的那样工作,因为我正在代码文件路径中搜索的文件有多个子目录。这导致“排序”无法按预期运行。示例如下(item=['/tmp/test/file1'、'/opt/Application/code/server/file1'])(item=['/tmp/test/file2'、'/opt/Application/app/server/file3'])(item=['/tmp/test/file3'、'/opt/Application/app/server/file2']),因此,它基本上是按照code\file\u path.files返回的整个文件路径排序,而不仅仅是文件名。是否有一种方法可以修剪文件名并基于此进行排序?需要一个选项来修剪文件名,排序,然后附加整个路径,最后复制或移动它。这对我来说仍然没有达到预期效果。这是将变量设置为无。谢谢你的帮助。我能够理解并在其他地方使用这段代码。这个更新的答案符合我的确切要求。如果可能的话,你可以看看查找模块的问题吗这是可行的,但是正如你提到的,有多个循环的开销
- name: Make a dict
  set_fact:
    files_dict: |
      {%- set out_dict = dict() -%}
      {%- for tmp_file in tmp_file_path.files|map(attribute='path')|list -%}
      {%-   do out_dict.update({tmp_file|basename: {'tmp': tmp_file}}) -%}
      {%- endfor -%}
      {%- for opt_file in code_file_path.files|map(attribute='path')|list -%}
      {%-   do out_dict[opt_file|basename].update({'opt': opt_file}) -%}
      {%- endfor -%}
      {{ out_dict }}

- name: Copy from dict
  copy:
    src: "{{ item.value.tmp }}"
    dest: "{{ item.value.opt }}"
    remote_src: yes
  with_dict: "{{ files_dict }}"