更有效的方式找到& tar数百万个文件

时间:2010-04-23 08:40:44

标签: bash find tar

我已经在命令行提示符下运行了两天的作业:

find data/ -name filepattern-*2009* -exec tar uf 2009.tar {} ;

永远 ,然后是一些。是的,目标目录中有数百万个文件。 (每个文件在一个良好的散列目录结构中只有8个字节。)但只是运行...

find data/ -name filepattern-*2009* -print > filesOfInterest.txt

......只需要两个小时左右。按照我的工作正在运行的速度,它将不会在几个完成。这似乎是不合理的。 这样做效率更高吗?也许使用更复杂的bash脚本?

第二个问题是“为什么我目前的做法如此缓慢?”

9 个答案:

答案 0 :(得分:24)

一种选择是使用 cpio 生成tar格式的存档:

$ find data/ -name "filepattern-*2009*" | cpio -ov --format=ustar > 2009.tar

cpio 本身使用来自stdin的文件名列表,而不是顶级目录,这使其成为这种情况的理想工具。

答案 1 :(得分:19)

如果您已经执行了创建文件列表的第二个命令,只需使用-T选项告诉tar从该保存的文件列表中读取文件名。运行1 tar命令和N tar命令会好很多。

答案 2 :(得分:8)

这是一个find-tar组合,可以在不使用xargs或exec的情况下做你想做的事情(这会导致显着的加速):

tar --version    # tar (GNU tar) 1.14 

# FreeBSD find (on Mac OS X)
find -x data -name "filepattern-*2009*" -print0 | tar --null --no-recursion -uf 2009.tar --files-from -

# for GNU find use -xdev instead of -x
gfind data -xdev -name "filepattern-*2009*" -print0 | tar --null --no-recursion -uf 2009.tar --files-from -

# added: set permissions via tar
find -x data -name "filepattern-*2009*" -print0 | \
    tar --null --no-recursion --owner=... --group=... --mode=... -uf 2009.tar --files-from -

答案 3 :(得分:7)

这有xargs:

find data/ -name filepattern-*2009* -print0 | xargs -0 tar uf 2009.tar
由于没有太多信息,猜测为什么它很慢很难。目录的结构是什么,您使用什么文件系统,如何在创建时配置它。对于大多数文件系统来说,在单个目录中拥有数百万个文件是非常困难的。

答案 4 :(得分:3)

要正确处理带有奇怪(但合法)字符(例如换行符,...)的文件名,您应该使用find的-print0:

将文件列表写入filesOfInterest.txt
find -x data -name "filepattern-*2009*" -print0 > filesOfInterest.txt
tar --null --no-recursion -uf 2009.tar --files-from filesOfInterest.txt 

答案 5 :(得分:2)

你现在拥有的东西,你每次找到一个文件时都会调用tar命令,这并不奇怪。而不是花两个小时打印加上打开tar存档所花费的时间,看看文件是否过时,并将它们添加到存档,实际上是将这些时间相乘。在将所有名称批处理后,可能会更好地调用tar命令一次,可能使用xargs来实现调用。顺便说一句,我希望你使用'filepattern- * 2009 *'而不是filepattern- * 2009 *因为星号将被shell扩展而没有引号。

答案 6 :(得分:1)

有一个名为tarsplitter的实用程序。

tarsplitter -m archive -i folder/*.json -o archive.tar -p 8

将使用8个线程将与“ folder / *。json”匹配的文件归档到“ archive.tar”的输出归档文件中。

https://github.com/AQUAOSOTech/tarsplitter

答案 7 :(得分:1)

我在Linux上苦苦挣扎了很长时间,然后才发现使用Python的tarfile库更简单,可能更快的解决方案。

  1. 使用glob.glob搜索所需的文件路径
  2. 以追加模式创建新的存档
  3. 将每个文件路径添加到该存档中
  4. 关闭档案文件

这是我的代码示例:

import tarfile
import glob
from tqdm import tqdm

filepaths = glob.glob("Images/7 *.jpeg")
n = len(filepaths)
print ("{} files found.".format(n))
print ("Creating Archive...")
out = tarfile.open("Images.tar.gz", mode = "a")
for filepath in tqdm(filepaths, "Appending files to the archive..."):
  try:
    out.add(filepath)
  except:
    print ("Failed to add: {}".format(filepath))

print ("Closing the archive...")
out.close()

这总共花费了大约12秒的时间来找到16222个文件路径并创建档案,但是,这主要是通过简单地搜索文件路径来解决的。创建具有16000个文件路径的tar存档仅用了7秒。使用一些多线程可以更快。

如果您正在寻找一种多线程实现,我已经做了一个并将其放在这里:

import tarfile
import glob
from tqdm import tqdm
import threading

filepaths = glob.glob("Images/7 *.jpeg")
n = len(filepaths)
print ("{} files found.".format(n))
print ("Creating Archive...")
out = tarfile.open("Images.tar.gz", mode = "a")

def add(filepath):
  try:
    out.add(filepath)
  except:
    print ("Failed to add: {}".format(filepath))

def add_multiple(filepaths):
  for filepath in filepaths:
    add(filepath)

max_threads = 16
filepaths_per_thread = 16

interval = max_threads * filepaths_per_thread

for i in tqdm(range(0, n, interval), "Appending files to the archive..."):
  threads = [threading.Thread(target = add_multiple, args = (filepaths[j:j + filepaths_per_thread],)) for j in range(i, min([n, i + interval]), filepaths_per_thread)]
  for thread in threads:
    thread.start()
  for thread in threads:
    thread.join()

print ("Closing the archive...")
out.close()

当然,您需要确保max_threadsfilepaths_per_thread的值已优化。创建线程需要花费时间,因此对于某些值,时间实际上可能增加。最后要注意的是,由于我们使用附加模式,因此如果不存在指定名称的存档,我们将自动创建它。但是,如果一个确实存在,它只会将其添加到先前存在的档案中,而不是对其进行重置或创建一个新的档案。

答案 8 :(得分:-2)

最简单(也可以在创建档案后删除文件):

find *.1  -exec tar czf '{}.tgz' '{}' --remove-files \;