如何在远程系统上使用 Ansible 任务移动/重命名文件

Posted

技术标签:

【中文标题】如何在远程系统上使用 Ansible 任务移动/重命名文件【英文标题】:How to move/rename a file using an Ansible task on a remote system 【发布时间】:2014-08-01 12:21:27 【问题描述】:

如何在远程系统上使用 Ansible 模块移动/重命名文件/目录?我不想使用命令/shell 任务,也不想将文件从本地系统复制到远程系统。

【问题讨论】:

你为什么不想使用命令/shell? 只是想知道是否有不使用上述任务的方法。目前看来没有其他办法了。 为什么要专门移动它而不是复制它?这似乎是一次性的操作,而不是幂等的确保系统状态类型的步骤。 我有一个包含在 RPM 包中的示例配置文件,我想移动这个示例配置文件。 目前我正在使用符号链接来引用该文件。使用 get_url 对我来说不是选项,因为系统无法访问互联网。 【参考方案1】:
- name: Example
  hosts: localhost
  become: yes

  tasks:
  - name: checking if a file exists
    stat:
      path: "/projects/challenge/simplefile.txt"
    register: file_data
  
  - name: move the file if file exists
    copy: 
      src: /projects/challenge/simplefile.txt
      dest: /home/user/test
    when: file_data.stat.exists

  - name: report a missing file
    debug: 
      msg: "the file or directory doesn't exist"
    when: not file_data.stat.exists

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。【参考方案2】:

另一个对我很有效的选项是使用 synchronize module 。然后使用文件模块删除原目录。

这是文档中的一个示例:

- synchronize:
    src: /first/absolute/path
    dest: /second/absolute/path
    archive: yes
  delegate_to: " inventory_hostname "

【讨论】:

这并不是在所有情况下都在本地工作,因为dest 是通过 SSH 访问的,即使目录在同一台机器上。【参考方案3】:

从 2.0 版开始,在copy module 中可以使用remote_src 参数。

如果True 它将转到远程/目标计算机的 src。

- name: Copy files from foo to bar
  copy: remote_src=True src=/path/to/foo dest=/path/to/bar

如果你想移动文件,你需要使用文件模块删除旧文件

- name: Remove old files foo
  file: path=/path/to/foo state=absent

从 2.8 版开始 copy module remote_src 支持递归复制。

【讨论】:

小注:“目前remote_src不支持递归复制。”取自 ansible 模块文档。所以如果你想递归复制,你仍然需要 shell/command 模块。 我不明白。复制然后删除与移动不同。一方面,它不是原子的。另一方面,它速度较慢,尤其是对于大文件。我是 Ansible 的新手,但这对我来说真的很奇怪。 @alex 我的意思是这不可能是正确的方法。我要逆风反对 50 点赞成票,但这太疯狂了。另一个问题:没有维护权限和其他属性。另:如果文件is changed during the copy呢? @Hamish Downer 和 mlissner。我并没有说它是满足您所有需求的最佳解决方案。我还写道,如果您想复制许多文件,则不应使用复制模块。阅读问题“我不想使用命令/shell 任务”。 @Alex 这是关于幂等移动文件的问题的第二高投票答案。问题不在于复制。复制而不是移动有很多问题,因此这个答案是不正确的。所以它得到了反对。 SO的礼仪是为了解释否决票。如其他地方所述,目前最好的选择是command: mv /path/to/foo /path/to/bar creates=/path/to/bar removes=/path/to/foo【参考方案4】:
- name: Move the src file to dest
  command: mv /path/to/src /path/to/dest
  args:
    removes: /path/to/src
    creates: /path/to/dest

这仅在/path/to/src 存在且/path/to/dest 不存在时运行mv 命令,因此它在每个主机上运行一次,移动文件,然后不再运行。

当我需要在数百台主机上移动文件或目录时,我使用此方法,其中许多主机可能在任何给定时间关闭。留在剧本中是幂等且安全的。

【讨论】:

【参考方案5】:

在 Windows 上: - name: Move old folder to backup win_command: "cmd.exe /c move /Y sourcePath destinationFolderPath "

要重命名,请改用 renameren 命令

【讨论】:

请解释 /c 和 /Y 的使用,我对重命名更感兴趣,并且能够在没有 /Y 的情况下使用它。我已经使用 cmd /? 浏览了帮助主题但在这里包含 /Y 并没有多大意义。 @user2964808, /c 是让cmd退出并返回控制,否则会等待用户输入 @user2964808, /Y 如果文件已经存在则跳过询问确认。如果您 100% 确定目标文件夹为空,则可以跳过它【参考方案6】:

我知道这是一个 YEARS 年的老话题,但我感到沮丧并为自己建立了一个角色来为任意文件列表执行此操作。随心所欲地扩展:

main.yml

- name: created destination directory
  file:
    path: /path/to/directory
    state: directory
    mode: '0750'
- include_tasks: move.yml
  loop:
    - file1
    - file2
    - file3

move.yml

- name: stat the file
  stat:
    path:  item 
  register: my_file

- name: hard link the file into directory
  file:
    src: /original/path/to/ item 
    dest: /path/to/directory/ item 
    state: hard
  when: my_file.stat.exists

- name: Delete the original file
  file:
    path: /original/path/to/ item 
    state: absent
  when: my_file.stat.exists

请注意,硬链接比在此处复制更可取,因为它固有地保留了所有权和权限(除了不会为文件的第二个副本消耗更多磁盘空间)。

【讨论】:

【参考方案7】:

你可以这样做——

使用临时命令

ansible all -m command -a" mv /path/to/foo /path/to/bar"

如果你想使用剧本来做到这一点,你也可以

- name: Move File foo to destination bar
  command: mv /path/to/foo /path/to/bar

【讨论】:

【参考方案8】:

这可能看起来有点矫枉过正,但如果你想避免使用命令模块(我这样做,因为它使用命令不是幂等的)你可以使用复制和取消归档的组合。

    使用 tar 归档您需要的文件。如果您提前考虑,这实际上是有道理的。您可能需要给定目录中的一系列文件。使用所有文件创建该目录并将它们存档在 tar 中。 使用取消归档模块。当您这样做时,连同 destination: 和 remote_src: 关键字,您可以将所有文件复制到一个临时文件夹以开始,然后将它们解压缩到您想要的位置。

【讨论】:

用tar归档没有幂等性【参考方案9】:

实现此目的的另一种方法是将filestate: hard 结合使用。

这是我开始工作的一个例子:

- name: Link source file to another destination
  file:
    src: /path/to/source/file
    path: /target/path/of/file
    state: hard

虽然只在 localhost (OSX) 上测试过,但也应该在 Linux 上工作。对于 Windows,我不知道。

请注意,需要绝对路径。否则它不会让我创建链接。此外,您不能跨文件系统,因此使用任何已挂载的媒体都可能会失败。

硬链接和移动非常相似,如果你之后删除源文件:

- name: Remove old file
  file:
    path: /path/to/source/file
    state: absent

另一个好处是,当您进行游戏时,更改会持续存在。因此,如果有人更改了源文件,任何更改都会反映在目标文件中。

您可以通过ls -l 验证文件的链接数量。硬链接的数量显示在模式旁边(例如 rwxr-xr-x 2,当文件有 2 个链接时)。

【讨论】:

不幸的是,这不适用于目录,因为目录不允许硬链接((( 这个答案对目标系统做了一个假设,特别是 src 和 dest 都在同一个分区上。这可能不是真的,因此不应使用此答案。【参考方案10】:

这就是我让它为我工作的方式:

  Tasks:
  - name: checking if the file 1 exists
     stat:      
      path: /path/to/foo abc.xts
     register: stat_result

  - name: moving file 1
    command: mv /path/to/foo abc.xts /tmp
    when: stat_result.stat.exists == True

上面的剧本,会在将文件移动到 tmp 文件夹之前检查文件 abc.xts 是否存在。

【讨论】:

无需使用when: stat_result.stat.exists == True。只需使用when: stat_result.stat.exists 就足够了。 我通常使用== True,因为我总是在找不到文件或== False时做一些事情。 根据Official documentation page of stat moduleexists属性返回一个boolean值。因此,如果您只输入when: stat_result.stat.exists,那么如果文件存在则满足条件,这对于when: stat_result.stat.exists == True 也是相同的,但有更多文本和不必要的条件检查。【参考方案11】:

Bruce 没有尝试统计目的地来检查是否移动文件(如果文件已经存在);他在尝试 mv 之前确保要移动的文件确实存在。

如果您和 Tom 一样,只在文件不存在的情况下才移动,我认为我们仍然应该将 Bruce 的检查整合到组合中:

- name: stat foo
  stat: path=/path/to/foo
  register: foo_stat

- name: Move foo to bar
  command: creates="path/to/bar" mv /path/to/foo /path/to/bar
  when: foo_stat.stat.exists

【讨论】:

【参考方案12】:

我发现命令模块中的创建选项很有用。这个怎么样:

- name: Move foo to bar
  command: creates="path/to/bar" mv /path/to/foo /path/to/bar

我曾经像 Bruce P 建议的那样使用 stat 执行 2 任务方法。现在我将其作为一项创建任务来完成。我认为这更清楚。

【讨论】:

或者,更好的是:command: mv /path/to/foo /path/to/bar creates=/path/to/bar removes=/path/to/foo【参考方案13】:

文件模块不会复制远程系统上的文件。 src 参数仅在创建文件符号链接时由文件模块使用。

如果您想完全在远程系统上移动/重命名文件,那么最好的办法是使用命令模块来调用适当的命令:

- name: Move foo to bar
  command: mv /path/to/foo /path/to/bar

如果你想变得花哨,那么你可以先使用 stat 模块来检查 foo 是否真的存在:

- name: stat foo
  stat: path=/path/to/foo
  register: foo_stat

- name: Move foo to bar
  command: mv /path/to/foo /path/to/bar
  when: foo_stat.stat.exists

【讨论】:

不使用命令模块,你唯一的选择就是编写自己的自定义模块。 标记为正确答案,因为这是当前复制远程文件的方式。 关于跳跃前的检查:是否有任何理由不使用command 模块的removes 选项(记录在here)?似乎该选项会首先进行 Ansible 检查。 Ansible 跟踪更改以通知处理程序,这使得该解决方案不是最佳的。 如果您使用removes: /path/to/foocreates: /path/to/bar,则不必手动检查文件是否存在。 @Fonant 已经提到这一点作为对另一个答案的评论,但由于这是已接受的答案,我想再次指出。

以上是关于如何在远程系统上使用 Ansible 任务移动/重命名文件的主要内容,如果未能解决你的问题,请参考以下文章

Ansible架构介绍及部署

ansible基本用法

ansible基本用法

Ansible常用模块

自动化工具Ansible

ansible模块及语法