如何将多个大小相似的文件压缩成多个有大小限制的存档

如何将多个大小相似的文件压缩成多个有大小限制的存档

我在使用 Ubuntu 16.04。

我有一个文件夹,里面有很多文本文件(差不多 12k)。我需要将它们全部上传到一个接受.tar.gz上传的网站,然后自动解压,但每个文件的大小限制为 10MB(10000KB)(因此每个文件都必须单独解压)。如果我将tar.gz所有这些文件都解压,则生成的文件大约为 72MB。

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

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

我怎样才能完成这两项任务?

我完全可以接受包含 GUI、CLI 或脚本的解决方案。我并不追求速度,我只需要完成它。

答案1

完全虽然它是一个拼凑的、快速粗略的草图,但在包含 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
  • 在 head 部分,设置要压缩的文件数量。实际上,总会有一个文件来处理剩下的几个“剩余文件”。
  • 使用包含文件的目录作为参数来运行它:

    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 建议的改进。谢谢很多

答案2

纯 shell 方法:

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:仅用于命名 tarball 的一个计数器。
  • for ((i=0; i<${#files[@]}; i+=$num)); do:迭代数组的值。$i初始化为0(数组的第一个元素)并增加$num。此过程持续到我们遍历完所有元素(文件)。
  • tar cvzf files$i.tgz -- ${files[@]:$i:$num}:在 bash 中,您可以使用 来获取数组切片(数组的一部分)${array[@]:start:length},因此${array[@]:2:3}将从第二个元素开始返回三个元素。在这里,我们获取一个从 的当前值开始的切片,长度$i$num个元素。--如果您的任何文件名可以以 开头,则需要-
  • ((k++)): 增量$k

相关内容