Como tar.gz muitos arquivos de tamanho semelhante em vários arquivos com um limite de tamanho

11

Estou no Ubuntu 16.04.

Eu tenho uma pasta com muitos arquivos de texto (quase 12k). Preciso fazer o upload de todos eles para um site que aceite .tar.gz uploads e depois os descomprima automaticamente, mas tem um limite de 10MB (10000KB) por arquivo (então, em particular, cada arquivo precisa ser descompactado por conta própria). Se eu tar.gz todos esses arquivos, o arquivo resultante é de cerca de 72MB.

O que eu gostaria de fazer é criar oito arquivos .tar.gz , cada um com tamanho / dimensão (estritamente) menor que 10.000 KB.

Como alternativa, pode-se supor que todos os arquivos acima têm aproximadamente a mesma dimensão, então eu gostaria de criar oito arquivos .tar.gz com mais ou menos a mesma quantidade de arquivos cada.

Como posso fazer alguma dessas duas tarefas?

Estou perfeitamente bem com uma solução que envolve GUI, CLI ou scripting. Eu não estou procurando por velocidade aqui, eu só preciso disso.

    
por dadexix86 06.11.2016 / 12:38

2 respostas

9

Totalmente patchwork e um esboço rápido e aproximado, mas testado em um diretório com 3000 arquivos, o script abaixo fez um trabalho extremamente rápido:

#!/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'
python3 /path/tocompress_split.py /directory/with/files/tocompress
'.join(map(str.encode, files))) proc.stdin.write(b'
#!/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'
python3 /path/tocompress_split.py /directory/with/files/tocompress chunksize
'.join(map(str.encode, files))) proc.stdin.write(b'
#!/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'
python3 /path/tocompress_split.py /directory/with/files/tocompress
'.join(map(str.encode, files))) proc.stdin.write(b'
#!/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'
python3 /path/tocompress_split.py /directory/with/files/tocompress chunksize
'.join(map(str.encode, files))) proc.stdin.write(b'%pre%') 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)
') 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)
') 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)
') 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)

Como usar

  • Salve-o em um arquivo vazio como compress_split.py
  • Na seção head, defina o número de arquivos para compactar. Na prática, sempre haverá mais um para cuidar dos poucos "restos" restantes.
  • Execute-o com o diretório com seus arquivos como argumento:

    %pre%

arquivos .tar.gz numerados serão criados no mesmo diretório onde os arquivos estão.

Explicação

O script:

  • lista todos os arquivos no diretório
  • cd está no diretório para evitar adicionar as informações do caminho ao arquivo tar
  • lê a lista de arquivos, agrupando-os pela divisão definida
  • compacta o (s) subgrupo (s) em arquivos numerados

EDITAR

Cria automaticamente pedaços por tamanho em mb

Mais sofisticado é usar o tamanho máximo (em mb) dos fragmentos como um argumento (segundo). No script abaixo, os fragmentos são gravados em um arquivo compactado assim que o fragmento atinge (passa) o limite.

Como o script é acionado pelos fragmentos, excedendo o limite, isso só funcionará se o tamanho de todos os arquivos for substancialmente menor que o tamanho do bloco.

O script:

%pre%

Para executar:

%pre%

... onde chunksize é o tamanho de entrada para o comando tar.

Neste, as melhorias sugeridas por @DavidFoerster estão incluídas. Muito obrigado !

    
por Jacob Vlijm 06.11.2016 / 13:49
6

Uma abordagem de shell pura:

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

Explicação

  • files=(*) : salve a lista de arquivos (também se houver algum diretório, mude para files=(*.txt) para obter apenas itens com uma extensão txt ) na matriz $files .
  • num=$((${#files[@]}/8)); : ${#files[@]} é o número de elementos na matriz $files . O $(( )) é a maneira (limitada) do bash de fazer aritmética. Portanto, este comando define $num para o número de arquivos dividido por 8.
  • k=1 : apenas um contador para nomear os tarballs.
  • for ((i=0; i<${#files[@]}; i+=$num)); do : iterar sobre os valores da matriz. $i é inicializado em 0 (o primeiro elemento da matriz) e incrementado em $num . Isso continua até que tenhamos passado por todos os elementos (arquivos).
  • tar cvzf files$i.tgz -- ${files[@]:$i:$num} : no bash, você pode obter uma fatia da matriz (parte de uma matriz) usando ${array[@]:start:length} , Então ${array[@]:2:3} retornará três elementos a partir da segunda. Aqui, estamos pegando uma fatia que começa no valor atual de $i e é $num elements long. O -- é necessário no caso de qualquer um dos seus nomes de arquivos poder começar com - .
  • ((k++)) : incremento $k
por terdon 06.11.2016 / 15:41