web-dev-qa-db-fra.com

Comment tar.gz de nombreux fichiers de taille similaire dans plusieurs archives avec une limite de taille

Je suis sur Ubuntu 16.04.

J'ai un dossier avec beaucoup de fichiers texte (presque 12k). Je dois tous les télécharger sur un site Web qui accepte .tar.gz puis les décompresse automatiquement, mais avec une limite de 10 Mo (10000 Ko) par fichier (chaque fichier doit donc être décompressé seul). Si je tar.gz tous ces fichiers, le fichier résultant est d'environ 72 Mo.

Ce que je voudrais faire est de créer huit fichiers .tar.gz, chacun de taille/dimension (strictement) inférieure à 10000 Ko.

Alternativement, on peut supposer que tous les fichiers ci-dessus ont approximativement la même dimension. Je voudrais donc créer huit fichiers .tar.gz avec plus ou moins la même quantité de fichiers.

Comment puis-je effectuer l'une de ces deux tâches?

Je vais parfaitement bien avec une solution qui implique une interface graphique, CLI ou des scripts. Je ne cherche pas la vitesse ici, j'ai juste besoin que ce soit fait.

11
dadexix86

Totalement un patchwork et une esquisse rapide et approximative, mais testé sur un répertoire de 3000 fichiers, le script ci-dessous effectue un travail extrêmement rapide:

#!/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)

Comment utiliser

  • Enregistrez-le dans un fichier vide sous le nom compress_split.py
  • Dans la section head, définissez le nombre de fichiers à compresser. En pratique, il y en aura toujours un de plus pour s'occuper des derniers "restants".
  • Exécutez-le avec le répertoire avec vos fichiers comme argument:

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

les fichiers numérotés .tar.gz seront créés dans le même répertoire que celui où se trouvent les fichiers.

Explication

Le scénario:

  • liste tous les fichiers du répertoire
  • les cd dans le répertoire pour empêcher l'ajout des informations de chemin au fichier tar
  • lit la liste des fichiers en les regroupant par division
  • compresse le ou les sous-groupes en fichiers numérotés

MODIFIER

Créer automatiquement des morceaux par taille en Mo

Plus sophistiqué consiste à utiliser la taille maximale (en Mo) des morceaux en tant que (second) argument. Dans le script ci-dessous, les morceaux sont écrits dans un fichier compressé dès que le morceau atteint (passe) le seuil.

Étant donné que le script est déclenché par les morceaux, dépassant le seuil, cela ne fonctionnera que si la taille de (tous) les fichiers est considérablement plus petite que la taille du morceau.

Le scénario:

#!/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)

Courir:

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

... où chunksize est la taille de entrée pour la commande tar.

Dans celui-ci, les améliorations suggérées par @DavidFoerster sont incluses. Merci beaucoup!

9
Jacob Vlijm

Une approche pure 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

Explication

  • files=(*): enregistre la liste des fichiers (y compris les répertoires, le cas échéant, passez à files=(*.txt) pour n’obtenir que les éléments portant l’extension txt) dans le tableau $files.
  • num=$((${#files[@]}/8));: ${#files[@]} est le nombre d'éléments dans le tableau $files. La $(( )) est la façon dont arithmétique de bash (limitée). Ainsi, cette commande définit $num sur le nombre de fichiers divisé par 8.
  • k=1: juste un compteur pour nommer les archives.
  • for ((i=0; i<${#files[@]}; i+=$num)); do: parcourez les valeurs du tableau. $i est initialisé à 0 (le premier élément du tableau) et incrémenté de $num. Cela continue jusqu'à ce que nous ayons parcouru tous les éléments (fichiers).
  • tar cvzf files$i.tgz -- ${files[@]:$i:$num}: en bash, vous pouvez obtenir une tranche de tableau (partie d'un tableau) en utilisant ${array[@]:start:length}, donc ${array[@]:2:3} renverra trois éléments à partir du deuxième. Ici, nous prenons une tranche qui commence à la valeur actuelle de $i et contient $num long éléments. Le -- est nécessaire au cas où l'un de vos noms de fichier puisse commencer par un -.
  • ((k++)): incrémenter $k
6
terdon