Answers:
只要您不跨文件系统边界移动文件,该操作就应该是安全的。这是由于机制“移动”实际上是如何完成的。
如果您mv
使用同一文件系统上的文件,则实际上不会触摸该文件,而只会更改文件系统条目。
$ mv foo bar
实际上做
$ ln foo bar
$ rm foo
这将为命名的文件(实际上是文件系统条目指向的inode)创建硬链接(第二个目录条目),并删除该条目。从现在开始,当删除时,还有第二个文件系统条目指向的inode,删除旧条目实际上并不会删除属于该inode的任何块。foo
bar
foo
foo
foo
foo
您的程序无论如何都会愉快地追加到文件,因为其打开的文件句柄指向文件的索引节点,而不是文件系统条目。
注意:如果您的程序在两次写入之间关闭并重新打开文件,则最终将使用旧的文件系统条目创建一个新文件!
跨文件系统移动:
如果跨文件系统边界移动文件,则情况将变得很糟。在这种情况下,您不能保证文件保持一致,因为mv
实际上
要么
$ cp /path/to/foo /path/to/bar
$ rm /path/to/foo
分别
$ touch /path/to/bar
$ cat < /path/to/foo > /path/to/bar
$ rm /path/to/foo
根据在编写应用程序期间复制是否到达文件结尾,可能会发生新文件中只有一行的一半的情况。
此外,如果您的应用程序没有关闭并重新打开旧文件,即使看上去已被删除,它也会继续写入旧文件:内核知道哪些文件已打开,尽管它会删除文件系统条目,在应用程序关闭其打开的文件句柄之前,不会删除旧文件的inode和关联的块。
file-system borders
什么?
既然您说您正在使用node.js,所以我假设您将使用fs.rename()
(或fs.renameSync()
)重命名文件。该node.js方法已记录为使用rename(2)系统调用,该调用不会以任何方式接触文件本身,而只是更改文件在文件系统中列出的名称:
“ rename()重命名文件,如果需要,可以在目录之间移动它。对该文件的任何其他硬链接(使用链接(2)创建的)都不会受到影响。oldpath的打开文件描述符也不会受到影响。”
特别是,请注意上面引用的最后一句话,即即使重命名后,所有打开的文件描述符(例如程序将用于写入文件)仍将继续指向该文件。因此,即使文件在同时写入的同时被重命名,也不会丢失或损坏数据。
正如Andreas Weise在他的回答中指出的那样,named(2)系统调用(因此fs.rename()
在node.js中)将无法跨越文件系统边界工作。因此,尝试以这种方式将文件移动到其他文件系统将完全失败。
Unix mv
命令尝试通过检测错误来隐藏此限制,而是通过将文件的内容复制到新文件并删除原始文件来移动文件。不幸的是,如果在写入文件时移动文件,这样移动文件确实会造成数据丢失的风险。因此,如果要安全地重命名可以同时写入的文件,则不要使用mv
(或者至少应确保新路径和旧路径在同一文件系统上)。
rename()
系统调用。因此,原始版本的mv
实际上确实调用link()
来创建硬链接,然后unlink()
删除原始名称。rename()
是在FreeBSD中添加的,以在内核中自动实现。