Come tar.gz molti file di dimensioni simili in più archivi con un limite di dimensioni


11

Sono su Ubuntu 16.04.

Ho una cartella con molti file di testo (quasi 12k). Devo caricarli tutti su un sito Web che accetta i .tar.gzcaricamenti e li decomprime automaticamente, ma ha un limite di 10 MB (10000 KB) per file (quindi, in particolare, ogni file deve essere decompresso da solo). Se ho tar.gztutti questi file il file risultante è di circa 72 MB.

Quello che vorrei fare è creare otto .tar.gzfile, ognuno di dimensioni / dimensioni (rigorosamente) inferiori a 10000 KB.

In alternativa, si può presumere che tutti i file sopra abbiano approssimativamente la stessa dimensione, quindi vorrei creare otto .tar.gzfile con più o meno la stessa quantità di file ciascuno.

Come posso svolgere una di queste due attività?

Sto perfettamente bene con una soluzione che coinvolge GUI, CLI o script. Non sto cercando velocità qui, ne ho solo bisogno.


Presumibilmente i file 12k che hai avranno modelli o caratteri ripetuti nei loro nomi. Potresti eventualmente taraggiungerli tutti iniziando con un certo schema fino a quando non li avrai tutti. Questo può essere facilmente scritto ma non garantisce che le dimensioni saranno inferiori a 9 MB di cui hai bisogno. Tuttavia, è possibile regolare manualmente le dimensioni di quei file troppo grandi suddividendoli ulteriormente.
Juan Antonio,

Risposte:


9

Totalmente patchwork e uno schizzo rapido e approssimativo, ma testato su una directory con 3000 file, lo script seguente ha fatto un lavoro estremamente veloce:

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

Come usare

  • Salvalo in un file vuoto come compress_split.py
  • Nella sezione head, imposta il numero di file in cui comprimere. In pratica, ce ne sarà sempre uno in più per prendersi cura dei pochi "resti" rimasti.
  • Eseguilo con la directory con i tuoi file come argomento:

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

i .tar.gzfile numerati verranno creati nella stessa directory in cui si trovano i file.

Spiegazione

Il copione:

  • elenca tutti i file nella directory
  • cd è nella directory per impedire l'aggiunta delle informazioni sul percorso al file tar
  • legge l'elenco dei file, raggruppandoli per la divisione impostata
  • comprime i sottogruppi in file numerati

MODIFICARE

Crea automaticamente blocchi per dimensione in mb

Più sofisticato è usare la dimensione massima (in mb) dei blocchi come (secondo) argomento. Nello script seguente, i blocchi vengono scritti in un file compresso non appena il blocco raggiunge (supera) la soglia.

Poiché lo script viene attivato dai blocchi, superando la soglia, questo funzionerà solo se la dimensione di (tutti) i file è sostanzialmente inferiore alla dimensione del blocco.

Il copione:

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

Correre:

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

... dove chunksize è la dimensione dell'input per il comando tar.

In questo, sono inclusi i miglioramenti suggeriti da @DavidFoerster. Grazie mille !


@ dadexix86, prego!
Jacob Vlijm,

Mi sono sbarazzato dell'invocazione della shell e ho usato direttamente un elenco di argomenti. Tuttavia, elenchi di argomenti di grandi dimensioni possono essere problematici e proverò a migliorare tarulteriormente l' invocazione fornendo l'elenco dei file sul flusso di input standard.
David Foerster,

Ciao @DavidFoerster, mi fido della tua intuizione, ma qual è il vantaggio?
Jacob Vlijm,

La maggior parte degli ambienti di runtime ha un limite (soft e hard) sulla lunghezza totale delle stringhe di argomenti di un comando che raggiungerai rapidamente quando operi su migliaia di file. Ecco perché tarti consente di specificare i file da aggiungere (o estrarre) sull'input standard con un'opzione appropriata.
David Foerster,

@DavidFoerster c'è comunque un problema, il secondo non funziona più. In realtà nessuno dei due lo fa ...
Jacob Vlijm,

6

Un approccio puramente 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

Spiegazione

  • files=(*): salva l'elenco dei file (anche le directory se presenti, cambia files=(*.txt)per ottenere solo cose con txtun'estensione) nell'array $files.
  • num=$((${#files[@]}/8));: ${#files[@]}è il numero di elementi nella matrice $files. È $(( ))il modo (limitato) di bash di fare l'aritmetica. Quindi, questo comando imposta $numil numero di file diviso per 8.
  • k=1 : solo un contatore per nominare i tarball.
  • for ((i=0; i<${#files[@]}; i+=$num)); do: scorre i valori dell'array. $iviene inizializzato su 0(il primo elemento dell'array) e incrementato di $num. Questo continua fino a quando non abbiamo esaminato tutti gli elementi (file).
  • tar cvzf files$i.tgz -- ${files[@]:$i:$num}: in bash, puoi ottenere una porzione di array (parte di un array) usando ${array[@]:start:length}, quindi ${array[@]:2:3}restituirà tre elementi a partire dal secondo. Qui, stiamo prendendo una sezione che inizia al valore corrente di $ied è $numlunga elementi. Il --è necessario nel caso in cui uno qualsiasi dei nomi di file in grado di iniziare con un -.
  • ((k++)) : incremento $k

Bello! La prima volta che ho visto un uso pratico degli intervalli di indice dell'array bash.
Joe,

Molto pulito e succinto. Per me, più comprensibile delle soluzioni Python sebbene entrambi siano abbastanza buoni. Ti chiedi come si confrontano tutti in termini di prestazioni?
DocSalvager,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.