5

我有以下问题。我有两个目录,分别称为 dir1 和 dir2。它们都填充有文件和子目录。它们都在同一个文件系统上。dir1 的内容是旧的,必须用 dir2 的内容替换。dir1 的内容可以丢弃。但是,我需要这种替换以原子方式发生。我目前的解决方案如下。

mv dir1 dir1_backup; mv dir2 dir1; rm -rf dir1_backup

如果第一个命令失败,第二个命令将不会运行。这很好。但是,如果第二个命令失败怎么办?dir1 不再存在,我处于无效状态。

实现这一目标的最佳方法是什么?可能吗?

4

3 回答 3

3

Linux 提供了renameat2带有标志的系统调用,RENAME_EXCHANGE它将交换 2 个对象:

renameat2(AT_FDCWD, "dir1", AT_FDCWD, "dir2", RENAME_EXCHANGE);
于 2020-08-29T12:38:16.743 回答
1

如果第一个失败是真的,我不认为你上面的断言第二个命令会失败。如果mv dir1 dir1_backup失败,则 dir1 仍然存在,并将mv dir2 dir1dir2 设为 dir1 的子目录。此外,由于您在第三个命令中传递-fto rm,因此它会默默地无法删除不存在的目录。

作为参考,三个 bash 运算符(假设这里是 bash)以及它们的作用:

  • ; 只需在第一次退出后执行以下命令,无论退出状态如何。
  • && 仅当前一个命令完全退出时(即退出状态为 0)才执行以下命令。
  • || 仅当前一个命令不干净地退出时才执行以下命令(即,退出代码非零。)

如果您希望每个命令的执行依赖于前一个命令的成功执行,那么您可能需要考虑以下内容:

    mv dir1 dir1_backup && mv dir2 dir1 && rm -rf dir1_backup

如果移动目录是您预期的失败模式,那么在继续删除旧目录内容之前测试移动的返回值或测试预期目录的存在可能是合理的。如果测试失败,则将旧目录内容移回。整体操作将失败,但至少您不会处于无效状态。例如:

    mv dir1 dir1_backup && \
    mv dir2 dir1 && \
    rm -rf dir1_backup || mv dir1_backup dir1

因为我们知道&&只有前一个命令成功退出才会执行下面的命令,所以只有成功mv dir2 dir1了才会执行mv dir1 dir1_backup

此外,rm -rf dir1_backup仅当将 dir2 移动到 dir1 成功时才会执行。但是,如果最后一个退出代码(mv dir2 dir1如果失败则返回的代码)不为零,则||操作符会导致mv dir1_backup dir1执行。

希望有帮助!让我知道是否需要澄清。

于 2013-06-14T17:16:04.603 回答
0

在 Linux 上,您可能能够摆脱在旧目录之上绑定挂载新目录的问题。但是,对于在旧目录中的目录(例如,它们当前的工作目录)上具有打开句柄的进程,您仍然会遇到问题。

mount --bind dir2 dir1

类似的技巧将允许您访问旧目录以进行清理。

如果此文件系统通过 NFS 导出,那么您可能需要确保将导出选项设置为允许遍历文件系统边界并在服务器上进行绑定挂载。

其他 *nix 具有类似的功能,但它们不是标准化的。

于 2019-11-01T15:18:22.930 回答