在Linux中将大量文件从一个目录复制到另一个目录

在Linux中将大量文件从一个目录复制到另一个目录

我有一个包含大约 280,000 个文件的目录。我想将它们移动到另一个目录。

如果我使用cpmv,我会收到错误“参数列表太长”。

如果我编写如下脚本

for file in ls *; do
   cp {source} to {destination} 
done

然后,由于该ls命令,其性能会下降。

我怎样才能做到这一点?

答案1

使用同步

$ rsync -a {source}/ {destination}/

例如

$ rsync -a /some/path/to/src/ /other/path/to/dest/

(请注意结尾的 /s)


注意:如果这是一个漫长的操作,并且您想要在复制过程中看到一些进度指示,您可以添加-v(详细)选项,然后列出正在复制的每个文件,或者考虑使用该--progress选项,以获得更简洁的进度输出。

答案2

我这里的答案缺少两个双胞胎,所以我又添加了一个。

虽然这让我想起添加另一个标准答案......

在此处输入图片描述

这里有两个问题:

我有一个包含大约 280,000 个文件的目录。

大多数工具都无法很好地处理如此多的文件。不仅仅是大多数 Linux 工具或 Windows 工具,还有相当多的程序。这可能包括您的文件系统。长期解决方案是“好吧,那就不要这样做”。如果您有不同的文件,但它们位于不同的目录中。如果不是,请预计未来会继续遇到问题。

话虽如此,让我们来谈谈你的实际问题:

如果我使用 cp 或 mv,则会收到错误“参数列表太长”

这是由于 shell 扩展了 * 而导致的。shell 为结果分配的空间有限,结果空间用完了。这意味着任何*由 shell 扩展的命令都会遇到同样的问题。您要么需要同时扩展较少的选项,要么使用其他命令。

遇到此问题时经常使用的一个替代命令是find。已经有几个答案展示了如何使用它,所以我不会重复所有这些。不过,我要指出\;和之间的区别+,因为这可以产生巨大的性能差异,并很好地与之前的扩展解释挂钩。

find /path/to/search --name "*.txt" -exec command {} \;

将在 path/to/search/ 下找到所有文件并使用它执行命令,但请注意*. 周围的引号,这会将 * 提供给命令。如果我们不封装或转义它,那么 shell 会尝试扩展它,我们会得到相同的错误。

最后,我想提一下 {}。这些括号会被 find 找到的内容替换。如果您以分号结束命令;(您需要从 shell 中转义分号,因此\;示例中有 's),则结果将逐一传递。这意味着您将执行 280000 个 mv 命令。每个文件一个。这可能会很慢。

或者,您可以以 结尾+。这将同时传递尽可能多的参数。如果 bash 可以处理 2000 个参数,则 find /path -name "*filetype" -exec some_move {}+ 将调用 some_move 命令约 140 次,每次使用 2000 个参数。这更有效率(即更快)。

答案3

你不需要 ls,你可以简单地使用

for file in *; do
    cp $file /your/dest
done

或者你可以这样做:

echo * | xargs -i cp {} /your/dest

答案4

就我的情况而言,cp和 都rsync太慢了,无法将大约 400 万个文件从 HDD 复制到 SSD,因此我按照以下方式进行操作(我的所有文件都是同一文件夹中的 .txt 文件,因此请根据自己的情况进行调整find):

cd /path/to/source/folder
find . -name '*.txt' -print >/tmp/test.manifest
tar -c -T /tmp/test.manifest | (cd /path/to/destination/folder; tar xfp -)

由于遇到错误,我不得不将文件名打印到临时文件中Argument list too long。使用tar显著提高了我的传输速度,尽管我可以假设那些不容易压缩的文件可能表现得不太好。

相关内容