安全的原子文件复制操作

2024-04-19 23:03:29 发布

您现在位置:Python中文网/ 问答频道 /正文

我需要将一个文件从一个位置复制到另一个位置,如果文件已经存在于目标位置(没有覆盖),我需要抛出一个异常(或者至少以某种方式识别)。

我可以先使用os.path.exists()进行检查,但在检查和复制之间的很短时间内无法创建文件是非常重要的。

有没有一种内置的方法可以做到这一点,或者有没有一种方法可以将一个动作定义为原子的?


Tags: 文件path方法目标定义osexists方式
2条回答

没有办法做到这一点;文件复制操作从来不是原子的,也没有办法实现它们。

但是你可以用一个随机的,临时的名字写这个文件,然后把它重命名。重命名操作必须是原子的。 如果该文件已经存在,则重命名将失败,您将得到一个错误。

[EDIT2]rename()只有在同一个文件系统中执行时才是原子的。安全的方法是在目标文件夹中创建新文件。

[编辑]关于重命名是否始终是原子的以及覆盖行为,有很多讨论。所以我挖掘了一些资源。

在Linux上,如果目标存在并且源和目标都是文件,那么目标将被自动覆盖(man page)。所以我错了。

但是rename(2)仍然保证,如果发生错误,原始文件或新文件将保持有效,因此该操作是原子的,因为它不会损坏数据。它不是原子的,因为它阻止两个进程同时执行相同的重命名,并且您可以预测结果。一个人会赢,但你不知道是哪一个。

在Windows上,如果另一个进程当前正在写入文件,则如果尝试打开该文件进行写入,则会出现错误,因此Windows的一个优点是。

如果在将操作写入磁盘时计算机崩溃,则文件系统的实现将决定损坏的数据量。应用程序对此无能为力。所以别再抱怨了:-)

也没有其他方法比这一个更好甚至更好。

你可以用文件锁定代替。但这只会让一切变得更加复杂,不会产生额外的优势(除了变得更加复杂,有些人确实认为这是一个巨大的优势,因为某些原因)。当你的文件在网络驱动器上时,你会添加很多漂亮的角盒。

可以将open(2)与模式O_CREAT一起使用,如果文件已经存在,则会使函数失败。但这不会阻止第二个进程删除文件并编写自己的副本。

或者可以创建一个锁目录,因为创建目录也必须是原子的。但那也买不到多少。你必须自己写锁定代码,并确保绝对,100%确保你真的,真的总是删除锁定目录,以防灾难-你不能

实际上,是实现这一点的一种方法,原子的和安全的,前提是所有参与者都以相同的方式进行。这是对lock-free whack-a-mole algorithm的改编,并不完全是琐碎的,所以请随意使用“no”作为一般答案;)

怎么办

  1. 检查文件是否已存在。如果是的话就停下来。
  2. Generate a unique ID
  3. 将源文件复制到具有临时名称的目标文件夹,例如<target>.<UUID>.tmp
  4. 重命名副本<target>-<UUID>.mole.tmp
  5. Look for any other files matching the pattern<target>-*.mole.tmp
    • 如果他们的UUID比你的大,attempt to delete it。(如果它不见了,不用担心。)
    • 如果他们的UUID比你的少,尝试删除你自己的。(再说一次,不用担心它是否消失了)从现在起,把他们的UUID当作是你自己的。
  6. 再次检查目标文件是否已存在。如果是,请尝试删除临时文件。(不用担心它是否消失了。请记住,您的UUID可能在步骤5中已更改。)
  7. 如果在步骤6中还没有尝试删除它,请尝试将临时文件重命名为其最终名称<target>。(如果它不见了,不用担心,跳回到第5步。)

你完了!

工作原理

假设每个候选源文件都是一个从洞里出来的鼹鼠。中途,它会暂停并将任何竞争的鼹鼠打回地面,然后再检查没有其他鼹鼠完全出现。如果你在脑子里想清楚了,你会发现只有一只鼹鼠能一路逃出去。为了防止这个系统livelocking,我们添加了一个总的顺序,鼹鼠可以在这个总的顺序上攻击哪个。砰!博士论文。

步骤4可能看起来不必要,为什么不首先使用该名称?然而,在第5步中,另一个过程可能会“采用”您的mole文件,并在第7步中使其成为赢家,因此您不必继续写出内容是非常重要的!在同一个文件系统上重命名是原子的,因此步骤4是安全的。

相关问题 更多 >