问题描述
我的 ubuntu 云服务器上有数百万张图片。当我使用 mv
命令移动一个包含 1200 万张图像的完整文件夹时,它几乎立即发生。但是,当我 mv
只有图像(不是文件夹)时,需要一些时间。有没有办法像文件夹一样快速移动所有图像?
这是正在发生的事情:
-
src 文件夹有 1200 万张图像,我使用
$ mv src ../dst
立即发生
-
在 src 文件夹中,我这样做是为了移动:
find -maxdepth 1 -name '*.jpg' -exec mv -t ../../dst/ {} +
这需要一些时间。
有没有办法加快第二个过程?
最佳思路
长话短说:没有
对于较小数量的文件,您不需要 find
但是,即使在这种简化和较小的情况下,如果您只是
mv *.jpg ../../dst/
这比一次移动整个目录需要更多的时间。
为什么?关键是要了解 mv
的作用。
简而言之,mv
将一个编号(标识目录或文件)从一个 inode(包含它的目录)移动到另一个 inode,并且这些索引在文件系统的日志或 FAT(如果文件系统就是这样实现的)。
如果源和目标在同一个文件系统上,则没有实际的数据移动,它只是改变了位置,即它们附加到的点。
所以,当你 mv
一个目录时,你就是在做这个操作一次。
但是当您移动 100 万个文件时,您正在执行此操作 100 万次。
举一个实际的例子,你有一棵有很多树枝的树。特别是,有一个节点附加了 100 万个分支。要砍掉这些分支并将它们移动到其他地方,您可以剪切它们中的每一个,从而进行 100 万次剪切,或者在节点之前剪切,从而仅进行一次剪切(这是移动文件和移动文件之间的区别)目录)。
次佳思路
它仍然会很慢,因为如前所述,文件系统必须将每个文件名重新链接到其新位置。
但是,您可以从现在的基础上加快速度。
您的 find 命令为每个文件运行一次 exec。因此它为 1200 万个文件启动了 1200 万次 mv
命令。这可以通过两种方式改进。
-
在末尾添加一个加号:
find -maxdepth 1 -name '*.jpg' -exec mv -t ../../dst/ +
检查 man-page 以确保它在您的find
版本中受支持。效果应该是运行一系列mv
命令,文件名与每个 命令行 上的文件名一样多。 -
一起使用
find
和xargs
。find -maxdepth 1 -name '*.jpg' -print0 | xargs -0 mv -t ../../dst/
-print0
将使用 NUL,即零字节来分隔文件名。这加上xargs -0
修复了xargs
否则会在文件名中使用空格的任何问题。xargs
命令将从find
命令读取文件名列表,并对尽可能多的文件名运行mv
命令。
第三种思路
您的困惑来自文件系统抽象,这使您相信文件夹以 tree-like 方式包含文件和其他文件夹。这实际上不是真的:文件系统中的所有文件和目录都位于同一级别,并以某种编号标识,具体取决于实现。目录只是包含其他文件列表的特殊文件。
当您在文件系统中使用 “move” 文件时,实际文件不会去任何地方。相反,目录中的列表会更新以反映更改。
mv src ../dst
将单个列表条目从目录 .
移动到目录 ../dst
,因此速度很快。
find -maxdepth 1 -name '*.jpg' -exec mv -t ../../dst/
必须移动数百万个条目,因此速度较慢。如果您只调用 mv
一次而不是每个文件一次,它可能会加速,并且 mv
命令本身可以优化为一步移动多个目录条目,但是没有办法让它像移动一个目录时一样快单个目录。
第四种思路
一个简化的答案
移动文件完成是 3 个步骤:
-
add() 将文件链接到目标文件夹的 inode 列表
-
检查链接是否添加成功
-
如果上述检查成功,则从源文件夹的 inode 列表中删除()链接。
对于文件或文件夹,此过程是相同的。显然,为 1 个文件执行此操作比为 100 个文件执行此操作快 100。
man link
是 add() man unlink
是 remove() mv
只是使用了上面那两个命令,并添加了一个检查in-between 以防止数据丢失。