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