tarfile - Accesso ad Archivi Tar

Scopo Accesso ad archivi Tar
Versione Python 2.3 e successive

Il modulo tarfile fornisce accesso in lettura e scrittura agli archivi UNIX tar, inclusi i file compressi. Oltre agli standard POSIX parecchie estensioni GNU tar sono supportate. Sono gestiti anche diversi tipi di file speciale UNIX (link hard e soft, nodi di dispositivo ecc.).

Verificare i File Tar

La funzione is_tarfile() restituisce un valore booleano che indica se il nome del file passato come parametro fa riferimento ad un file tar valido.

import tarfile

for filename in [ 'LEGGIMI.txt', 'esempio.tar',
                  'cattivo_esempio.tar', 'nonqui.tar' ]:
    try:
        print '%20s  %s' % (filename, tarfile.is_tarfile(filename))
    except IOError, err:
        print '%20s  %s' % (filename, err)

Si noti che se il file non esiste, is_tarfile() solleva un IOError.

$ python tarfile_is_tarfile.py          LEGGIMI.txt  True
         esempio.tar  True
 cattivo_esempio.tar  False
          nonqui.tar  [Errno 2] No such file or directory: 'nonqui.tar'

Leggere Meta-dati da un Archivio

Per lavorare direttamente con un archivio tar si usa la classe TarFile. Essa supporta metodi per leggere dati su archivi esistenti così come la modifica di archivi aggiungendo ulteriori file.

Per leggere i nomi dei file in un archivio esistente si usa getnames().

import tarfile

t = tarfile.open('esempio.tar', 'r')
print t.getnames()

Il valore di ritorno è una lista di stringhe con i nomi del contenuto dell'archivio.

$ python tarfile_getnames.py
['LEGGIMI.txt']

Oltre ai nomi, i meta-dati circa i membri dell'archivio sono disponibili come istanze di oggetti Tarinfo. I meta-dati si caricano tramite getmembers() e getmember().

import tarfile
import time

t = tarfile.open('esempio.tar', 'r')
for member_info in t.getmembers():
    print member_info.name
    print '\tModificato:\t', time.ctime(member_info.mtime)
    print '\tModalità  :\t', oct(member_info.mode)
    print '\tTipo      :\t', member_info.type
    print '\tDimensione:\t', member_info.size, 'bytes'
    print
$ python tarfile_getmembers.py
LEGGIMI.txt
	Modificato:	Tue Mar 16 20:33:22 2010
	Modalità  :	0644
	Tipo      :	0
	Dimensione:	31 bytes

Se si conosce in anticipo il nome del membro dell'archivio si può ottenere il suo oggetto Tarinfo con getmember().

import tarfile
import time

t = tarfile.open('esempio.tar', 'r')
for filename in [ 'LEGGIMI.txt', 'nonqui.txt' ]:
    try:
        info = t.getmember(filename)
    except KeyError:
        print "ERRORE: Non trovato %s nell'archivio tar" % filename
    else:
        print '%s è di %d bytes' % (info.name, info.size)

Se il membro dell'archivio non è presente, getmember() solleva un KeyError.

$ python tarfile_getmember.py
LEGGIMI.txt è di 31 bytes
ERRORE: Non trovato nonqui.txt nell'archivio tar

Estrarre File da un Archivio

Per accedere ai dati da un membro di un archivio all'interno del proprio programma, si usa il metodo extractfile(), passandogli il nome del membro.

import tarfile

t = tarfile.open('esempio.tar', 'r')
for filename in [ 'LEGGIMI.txt', 'nonqui.txt' ]:
    try:
        f = t.extractfile(filename)
    except KeyError:
        print "ERRORE: Non trovato %s nell'archivio tar" % filename
    else:
        print filename, ':', f.read()
$ python tarfile_extractfile.py
LEGGIMI.txt : Gli esempi per il modulo tarfile usano questo file ed esempio.tar come dati.

ERRORE: Non trovato nonqui.txt nell'archivio tar

Se si vuole semplicemente spacchettare l'archivio e scrivere i file nel filesystem, si usano invece extract() oppure extractall().

import tarfile
import os

os.mkdir('outdir')
t = tarfile.open('esempio.tar', 'r')
t.extract('LEGGIMI.txt', 'outdir')
print os.listdir('outdir')
$ python tarfile_extractall.py
['LEGGIMI.txt']
La documentazione della libreria standard comprende una nota che afferma che extractall() è più sicuro di extract(), quindi dovrebbe essere usato nella maggior parte dei casi.
import tarfile
import os

os.mkdir('outdir')
t = tarfile.open('esempio.tar', 'r')
t.extractall('outdir')
print os.listdir('outdir')

Se si vuole estrarre solo alcuni file dall'archivio, i loro nomi possono essere passati a extractall().

import tarfile
import os

os.mkdir('outdir')
t = tarfile.open('esempio.tar', 'r')
t.extractall('outdir', members=[t.getmember('LEGGIMI.txt')])
print os.listdir('outdir')
$ python tarfile_extractall.py
['LEGGIMI.txt']

Creare Nuovi Archivi

Per creare un nuovo archivio, si apre semplicemente TarFile in modalità 'w'. Un qualsiasi file esistente viene troncato e viene creato un nuovo archivio. Per aggiungere dei file si usa il metodo add().

import tarfile

print 'creazione archivio'
out = tarfile.open('tarfile_aggiunto.tar', mode='w')
try:
    print 'aggiunta di LEGGIMI.txt'
    out.add('LEGGIMI.txt')
finally:
    print 'chiusura'
    out.close()

print
print 'Contenuto:'
t = tarfile.open('tarfile_aggiunto.tar', 'r')
for member_info in t.getmembers():
    print member_info.name
$ python tarfile_add.py
creazione archivio
aggiunta di LEGGIMI.txt
chiusura

Contenuto:
LEGGIMI.txt

Usare Nomi Alternativi per i Membri di un Archivio

E' possibile aggiungere un file ad un archivio usando un nome diverso da quello del file originale, costruendo un oggetto Tarinfo con un nome alternativo assegnato al parametro arcname che viene quindi passato ad addfile().

import tarfile

print 'creazione archivio'
out = tarfile.open('tarfile_aggiungifile.tar', mode='w')
try:
    print 'aggiunto LEGGIMI.txt come RINOMINATO.txt'
    info = out.gettarinfo('LEGGIMI.txt', arcname='RINOMINATO.txt')
    out.addfile(info)
finally:
    print 'chiusura'
    out.close()

print
print 'Contenuto:'
t = tarfile.open('tarfile_aggiungifile.tar', 'r')
for member_info in t.getmembers():
    print member_info.name

L'archivio comprende solo il file con il nome cambiato.

$ python tarfile_addfile.py
creazione archivio
aggiunto LEGGIMI.txt come RINOMINATO.txt
chiusura

Contenuto:
RINOMINATO.txt

Scrivere Dati da Sorgenti Diverse Dai File

Talvolta si vuole scrivere dati ad un archivio, ma gli stessi non sono rappresentati da un file nel filesystem. Invece che scrivere dei dati ad un file, quindi aggiungere quel file all'archivio, si può usare addfile() per aggiungere dati da un handle di file aperto.

import tarfile
from cStringIO import StringIO

data = "Questi sono i dati da scrivere nell'archivio."

out = tarfile.open('tarfile_aggiungifile_stringa.tar', mode='w')
try:
    info = tarfile.TarInfo('made_up_file.txt')
    info.size = len(data)
    out.addfile(info, StringIO(data))
finally:
    out.close()

print
print 'Contenuto:'
t = tarfile.open('tarfile_aggiungifile_stringa.tar', 'r')
for member_info in t.getmembers():
    print member_info.name
    f = t.extractfile(member_info)
    print f.read()

Se si costruisce autonomamente un oggetto Tarinfo, si può assegnare al membro dell'archivio un qualsiasi nome a scelta. Dopo averne impostata la dimensione, si possono scrivere i dati nell'archivio usando addfile(), e passando un buffer StringIO come sorgente dei dati.

$ python tarfile_addfile_string.py

Contenuto:
made_up_file.txt
Questi sono i dati da scrivere nell'archivio.

Aggiungere agli Archivi

Oltre alla creazione di nuovi archivi, è possibile aggiungere dati ad un archivio esistente. Per farlo si apre il file usando la modalità 'a'.

import tarfile

print 'creazione archivio'
out = tarfile.open('tarfile_accoda.tar', mode='w')
try:
    out.add('LEGGIMI.txt')
finally:
    out.close()

print 'contenuto:', [m.name
                    for m in tarfile.open('tarfile_accoda.tar', 'r').getmembers()]

print 'accodo index.rst'
out = tarfile.open('tarfile_accoda.tar', mode='a')
try:
    out.add('index.rst')
finally:
    out.close()

print 'contenuto:', [m.name
                    for m in tarfile.open('tarfile_accoda.tar', 'r').getmembers()]

L'archivio che ne deriva conterrà due membri.

$ python tarfile_append.py
creazione archivio
contenuto: ['LEGGIMI.txt']
accodo index.rst
contenuto: ['LEGGIMI.txt', 'index.rst']

Lavorare con Archivi Compressi

Oltre ai normali archivi tar, il modulo tarfile può lavorare anche con archivi compressi tramite i protocolli gzip e bzip2. Per aprire un archivio compresso, si modifica la stringa della modalità passata ad open() per includere ":gzip" oppure :bz2, a seconda del metodo di compressione che si intende usare.

import tarfile
import os

fmt = '%-30s %-10s'
print fmt % ('NOME FILE', 'DIMENSIONE')
print fmt % ('LEGGIMI.txt', os.stat('LEGGIMI.txt').st_size)

for filename, write_mode in [
    ('tarfile_compressione.tar', 'w'),
    ('tarfile_compressione.tar.gz', 'w:gz'),
    ('tarfile_compressione.tar.bz2', 'w:bz2'),
    ]:
    out = tarfile.open(filename, mode=write_mode)
    try:
        out.add('LEGGIMI.txt')
    finally:
        out.close()

    print fmt % (filename, os.stat(filename).st_size),
    print [m.name for m in tarfile.open(filename, 'r:*').getmembers()]

Quando si apre un archivio esistente in lettura, si può specificare "r:*" affinchè tarfile possa determinare il metodo di compressione da usare automaticamente.

$ python tarfile_compression.py
NOME FILE                      DIMENSIONE
LEGGIMI.txt                    77
tarfile_compressione.tar       10240      ['LEGGIMI.txt']
tarfile_compressione.tar.gz    210        ['LEGGIMI.txt']
tarfile_compressione.tar.bz2   194        ['LEGGIMI.txt']

Vedere anche:

tarfile
La documentazione della libreria standard per questo modulo.
GNU tar manual
Documentazione del formato tar, incluse le estensioni (in inglese).
zipfile
Accesso similare agli archivi ZIP
gzip
Compressione GNU zip
bz2
Compressione bz2