我已经在命令行提示符下运行了两天的作业:
find data/ -name filepattern-*2009* -exec tar uf 2009.tar {} ;
永远 ,然后是一些。是的,目标目录中有数百万个文件。 (每个文件在一个良好的散列目录结构中只有8个字节。)但只是运行...
find data/ -name filepattern-*2009* -print > filesOfInterest.txt
......只需要两个小时左右。按照我的工作正在运行的速度,它将不会在几个周完成。这似乎是不合理的。 这样做效率更高吗?也许使用更复杂的bash脚本?
第二个问题是“为什么我目前的做法如此缓慢?”
答案 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.txtfind -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”的输出归档文件中。
答案 7 :(得分:1)
我在Linux上苦苦挣扎了很长时间,然后才发现使用Python的tarfile库更简单,可能更快的解决方案。
这是我的代码示例:
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_threads
和filepaths_per_thread
的值已优化。创建线程需要花费时间,因此对于某些值,时间实际上可能增加。最后要注意的是,由于我们使用附加模式,因此如果不存在指定名称的存档,我们将自动创建它。但是,如果一个确实存在,它只会将其添加到先前存在的档案中,而不是对其进行重置或创建一个新的档案。
答案 8 :(得分:-2)
最简单(也可以在创建档案后删除文件):
find *.1 -exec tar czf '{}.tgz' '{}' --remove-files \;