为什么移动文件夹中的某些文件比移动整个文件夹要花更长的时间?


21

我的ubuntu云服务器上有数百万个图像。当我使用mv命令移动包含1200万张图像的完整文件夹时,它几乎是瞬间发生的。但是,当我mv仅图像(而不是文件夹)时,则需要一些时间。有没有办法像文件夹一样快地移动所有图像?

这是正在发生的事情:

  1. src文件夹中有1200万张图片,我使用

    $ mv  src ../dst
    

    立即发生

  2. 在src文件夹中,我这样做是为了移动:

    find -maxdepth 1 -name '*.jpg' -exec mv -t ../../dst/ {} +
    

    这需要一些时间。

有没有办法加快第二个过程?


1
不是解决方案-而是要澄清:cmd2必须比cmd1慢,因为它正在使用find,然后执行结果移动。如果没有预查找过程,这绝对不可能像直接行动那样快。
dufte

可能dst在一个分区中,而../../dst在另一个分区中。
phuclv

按照书面说明,这甚至看起来都不是有效的查找调用。它缺少任何{}扩展文件名的参数。
R ..

我提交了一项修改标题的编辑,删除了对“图像”的引用,并将其替换为一个小问题-移动单个文件与移动整个文件夹。我希望做到这一点的代表会接受它。
Monty Harder

1
这不是的有效调用find。每个文件find ... -exec mv -t ../../dst/ {} \;调用mv一次;find ... -exec mv -t ../../dest {} +这样会更快,每次调用可以复制尽可能多的文件,但仍不如dadexix86解释的那样移动目录本身。
chepner '16

Answers:


50

TL; DR:否

对于少量文件,find即使只是在这种简化和较小的情况下,也不需要

mv *.jpg ../../dst/

比一次移动整个目录要花费更多时间。


为什么?关键是要了解做什么mv

简而言之,mv将一个数字(标识目录或文件)从一个索引节点(包含索引的目录)移动到另一个索引,并且这些索引在文件系统的日志或FAT(如果文件系统)中更新以这种方式实现)。

如果源和目标位于同一文件系统上,则没有实际的数据移动,它只是更改位置(即数据连接到的位置)。

所以,当你mv 一个目录,你在做此操作一次

但是,当您移动一百万个文件时,您将执行一百万次此操作。

为了给您一个实际的例子,您有一棵有许多分支的树。特别是,有一个节点附有一百万个分支。
要削减这些分支并将其移动到其他位置,您可以削减其中的每个分支,这样一来就削减了100万,或者您就在节点之前进行了削减,因此只进行了一次削减(这是移动文件和删除文件之间的区别。目录)。


4
您应该包括mv在同一文件系统上的只是重写TOC条目。
Videonauth

我不确定我是否理解您对TOC的理解。据我所知,ext文件系统,NTFS或btrfs等中都没有表。FAT有一个表(从中取其名称),但例如ext在inode中存储名称和块,父项,子项和其他信息。如果您能指向我参考一些参考资料,其中解释了ext FS在何处具有其TOC及其用途,我将很高兴阅读并更新答案:)
dadexix86

10
mv *.jpg很可能导致1200万个文件失败,这就是他使用find的原因。我相信包括Linux在内的大多数Unix(除非有人在最近5-10年内对其进行了更改),命令行的最大长度是有限的。我认为很长时间以来Linux都是64K。我敢肯定,同样的限制也适用于环境变量。
Zan Lynx'6

1
移动文件更多的是关于移动文件。类似Unix的目录条目包含一个文件名和一个inode编号,该编号基本上是指向其余元数据的指针。目录只是一种特殊的文件。索引节点本身并不包含文件的实际数据,而仅包含指向该文件的指针,因此说任何东西都从索引节点中移出有点误导。另一方面,文件系统日志通常指一种主要用于防崩溃的元数据日志类型。
ilkkachu

1
当然,术语不是这里的重点。重要的一点就是您所说的:在文件系统内,移动仅需触摸元数据。从一个文件系统到另一个文件系统,没有快捷方式,所有文件(包括其内容)都需要一个一个地移动(重新创建)。在这种情况下,移动整个目录还是仅移动其中的文件都没有关系,它将变得同样慢。
ilkkachu

13

仍然很慢,因为如上所述,文件系统必须将每个文件名重新链接到其新位置。

但是,您可以从现在开始加快速度。

您的find命令为每个文件运行一次exec。因此,它启动了mv为1200万个文件 1200万次命令。这可以通过两种方式进行改进。

  • 在末尾添加一个加号:
    find -maxdepth 1 -name '*.jpg' -exec mv -t ../../dst/ +
    检查手册页以确保您的版本中支持该手册页find。效果应该是运行一系列mv命令,并在每个命令行上容纳尽可能多的文件名。

  • 使用findxargs在一起。
    find -maxdepth 1 -name '*.jpg' -print0 | xargs -0 mv -t ../../dst/
    -print0会用NUL,又名零个字节来分隔文件名。此加号xargs -0修复xargs了文件名中否则会出现的任何问题。该xargs命令将从命令中读取文件名列表,findmv在适合的任意多个文件名上运行该命令。


7

您的困惑来自文件系统抽象,它使您相信文件夹以树状方式包含文件和其他文件夹。实际上并非如此:文件系统中的所有文件和目录都位于同一级别,并根据实现方式用某种编号标识。目录只是包含其他文件列表的特殊文件。

当您“移动”文件系统中的文件时,实际文件不会随处可见。而是,目录内的列表会更新以反映更改。

mv src ../dst将单个列表条目从directory .移到directory ../dst,所以速度很快。

find -maxdepth 1 -name '*.jpg' -exec mv -t ../../dst/必须移动数百万个条目,因此速度较慢。如果mv仅对每个文件调用一次而不是一次调用,则可能会加快速度,并且mv命令本身可能已优化为一步移动几个目录条目,但是没有办法像移动单个目录一样快。


4

简化答案

移动文件完成了3个步骤:

  • add()指向目标文件夹的索引节点列表的文件链接
  • 检查链接是否成功添加
  • 如果上面的检查成功,则从源文件夹的索引节点列表中删除链接。

对于文件或文件夹,此过程相同。
很明显,对1个文件执行此操作比对100个文件执行此操作快100。

man link 是add()
man unlink是remove()
mv只是使用上述两个命令,并在两者之间添加了一个检查以防止数据丢失。


1
好了,还有rename()。
ilkkachu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.