如何将许多大小相似的文件tar.gz放入具有大小限制的多个档案中


11

我在Ubuntu 16.04上。

我有一个包含很多文本文件的文件夹(近12k)。我需要将它们全部上传到可以接受.tar.gz上传然后自动解压缩的网站,但每个文件的限制为10MB(10000KB)(因此,每个文件都必须自行解压缩)。如果我tar.gz所有这些文件,生成的文件约为72MB。

我想做的是创建八个.tar.gz文件,每个文件的大小/尺寸(严格地)小于10000KB。

或者,可以假设上述所有文件的尺寸都大致相同,因此我想创建八个.tar.gz文件,每个文件的数量大致相同。

我该怎么做这两项任务?

对于涉及GUI,CLI或脚本的解决方案,我做的很好。我不是在这里寻找速度,我只需要完成它。


大概您拥有的12k文件的名称中会有图案或重复的字符。您可以tar通过添加以特定模式开头的所有文件,直到拥有全部文件来实现它们。这可以很容易地编写脚本,但是不能保证大小会根据需要小于9MB。但是,您可以通过进一步拆分来手动调整太大的文件的大小。
Juan Antonio

Answers:


9

完全是拼凑而成,并且是一个快速,粗略的草图,但是在包含3000个文件的目录上进行了测试,以下脚本可以非常快地完成工作:

#!/usr/bin/env python3
import subprocess
import os
import sys

splitinto = 2

dr = sys.argv[1]
os.chdir(dr)

files = os.listdir(dr)
n_files = len(files)
size = n_files // splitinto

def compress(tar, files):
    command = ["tar", "-zcvf", "tarfile" + str(tar) + ".tar.gz", "-T", "-", "--null"]
    proc = subprocess.Popen(command, stdin=subprocess.PIPE)
    with proc:
        proc.stdin.write(b'\0'.join(map(str.encode, files)))
        proc.stdin.write(b'\0')
    if proc.returncode:
        sys.exit(proc.returncode)

sub = []; tar = 1
for f in files:
    sub.append(f)
    if len(sub) == size:
        compress(tar, sub)
        sub = []; tar += 1

if sub:
    # taking care of left
    compress(tar, sub)

如何使用

  • 将其另存为一个空文件 compress_split.py
  • 在头部部分,设置要压缩到的文件数。在实践中,总会有一个人来照顾剩下的几个“剩菜”。
  • 使用带有文件作为参数的目录运行它:

    python3 /path/tocompress_split.py /directory/with/files/tocompress

编号.tar.gz文件将在与文件所在目录相同的目录中创建。

说明

剧本:

  • 列出目录中的所有文件
  • cd进入目录,以防止将路径信息添加到tar文件中
  • 读取文件列表,按设置的分区对它们进行分组
  • 将子组压缩为编号文件

编辑

根据mb大小自动创建块

更复杂的是将块的最大大小(以mb为单位)用作(第二个)参数。在下面的脚本中,只要块达到(超过)阈值,就将它们写入压缩文件中。

由于脚本是由块触发的,超过了阈值,因此只有在(所有)文件的大小显着小于块大小的情况下,此脚本才起作用。

剧本:

#!/usr/bin/env python3
import subprocess
import os
import sys

dr = sys.argv[1]
chunksize = float(sys.argv[2])
os.chdir(dr)

files = os.listdir(dr)
n_files = len(files)

def compress(tar, files):
    command = ["tar", "-zcvf", "tarfile" + str(tar) + ".tar.gz", "-T", "-", "--null"]
    proc = subprocess.Popen(command, stdin=subprocess.PIPE)
    with proc:
        proc.stdin.write(b'\0'.join(map(str.encode, files)))
        proc.stdin.write(b'\0')
    if proc.returncode:
        sys.exit(proc.returncode)

sub = []; tar = 1; subsize = 0
for f in files:
    sub.append(f)
    subsize = subsize + (os.path.getsize(f)/1000000)
    if subsize >= chunksize:
        compress(tar, sub)
        sub = []; tar += 1; subsize = 0

if sub:
    # taking care of left
    compress(tar, sub)

跑步:

python3 /path/tocompress_split.py /directory/with/files/tocompress chunksize

...其中chunksize是tar命令的输入大小。

在这一本书中,包含了@DavidFoerster提出的建议改进。感谢了很多


@ dadexix86不客气!
Jacob Vlijm '16

我完全摆脱了shell调用,直接使用了参数列表。尽管如此,大型参数列表可能还是有问题的,我将尝试tar通过在标准输入流上提供文件列表来进一步改善调用。
大卫·佛斯特

嗨,@ DavidFoerster,我相信您的见解,但是优点是什么?
Jacob Vlijm '16

大多数运行时环境对命令的参数字符串的总长度有(软和硬)限制,在处理成千上万个文件时,您会很快达到该限制。因此tar,您可以使用适当的选项指定要在标准输入上添加(或提取)的文件。
大卫·佛斯特

@DavidFoerster虽然有问题,但第二个不再运行。实际上,他们俩都没有...
Jacob Vlijm '16

6

纯壳方法:

files=(*); 
num=$((${#files[@]}/8));
k=1
for ((i=0; i<${#files[@]}; i+=$num)); do 
    tar cvzf files$k.tgz -- "${files[@]:$i:$num}"
    ((k++))
done

说明

  • files=(*):在数组中保存文件列表(如果有的话,也包含目录,更改为files=(*.txt)仅获取带有txt扩展名的内容)$files
  • num=$((${#files[@]}/8));${#files[@]}是数组中元素的数量$files。该$(( ))是做算术的bash的(有限)的方式。因此,此命令设置$num为文件数除以8。
  • k=1 :只是一个用来命名压缩包的计数器。
  • for ((i=0; i<${#files[@]}; i+=$num)); do:迭代数组的值。$i0(数组的第一个元素)处初始化,并增加$num。这一直持续到我们遍历所有元素(文件)。
  • tar cvzf files$i.tgz -- ${files[@]:$i:$num}在bash中,您可以使用来获取数组切片(数组的一部分)${array[@]:start:length},因此${array[@]:2:3}将从第二个开始返回三个元素。在这里,我们正在开始于当前值的切片$i,是$num元素长。在--需要的情况下,你的任何文件名可以用一个开始-
  • ((k++)) : 增量 $k

真好!我第一次看到了bash数组索引范围的实际使用。

非常干净简洁。对我来说,虽然两者都相当不错,但比Python解决方案更容易理解。想知道它们在性能上如何进行比较?
DocSalvager '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.