tarfile - Accesso ad Archivi Tar

Scopo: Accesso ad archivi Tar

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 tipo collegamenti fisici> e simbolici, nodi di dispositivo.

Verificare i File Tar

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

# tarfile_is_tarfile.py

import tarfile

for filename in ['LEGGIMI.txt', 'esempio.tar',
                 'cattivo_esempio.tar', 'nonqui.tar']:
    try:
        print('{:>15}  {}'.format(filename, tarfile.is_tarfile(
            filename)))
    except IOError as err:
        print('{:>15}  {}'.format(filename, err))

Se il file non esiste, is_tarfile() solleva una eccezione IOError.

$ python3 tarfile_is_tarfile.py

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

Leggere Metadati da un File Tar

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

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

# tarfile_getnames.py

import tarfile

with tarfile.open('esempio.tar', 'r') as t:
    print(t.getnames())

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

$ python3 tarfile_getnames.py

['LEGGIMI.txt']

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

# tarfile_getmembers.py

import tarfile
import time

with tarfile.open('esempio.tar', 'r') as t:
    for member_info in t.getmembers():
        print(member_info.name)
        print('  Modificato:', time.ctime(member_info.mtime))
        print('  Modalità  :', oct(member_info.mode))
        print('  Tipo      :', member_info.type)
        print('  Dimensione:', member_info.size, 'byte')
        print()
$ python3 tarfile_getmembers.py

LEGGIMI.txt
  Modificato: Tue Mar 16 21:15:58 2010
  Modalità  : 0o644
  Tipo      : b'0'
  Dimensione: 77 byte

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

# tarfile_getmember.py

import tarfile
import time

with tarfile.open('esempio.tar', 'r') as t:
    for filename in [ 'LEGGIMI.txt', 'nonqui.txt' ]:
        try:
            info = t.getmember(filename)
        except KeyError:
            print("ERROE:  {} non trovato nell'archivio tar".format(
                filename))
        else:
            print("{} è {:d} byte".format(
                info.name, info.size))

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

$ python3 tarfile_getmember.py

LEGGIMI.txt è 77 byte
ERROE:  nonqui.txt non trovato nell'archivio tar

Estrarre File da un Archivio

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

# tarfile_extractfile.py

import tarfile

with tarfile.open('esempio.tar', 'r') as t:
    for filename in [ 'LEGGIMI.txt', 'nonqui.txt' ]:
        try:
            f = t.extractfile(filename)
        except KeyError:
            print("ERROR: {} non trovato nell'archivio tar".format(
                filename))
        else:
            print(filename, ':')
            print(f.read().decode('utf-8'))

Il valore ritornato è un oggetto tipo file dal quale è possibile leggere il contenuto del membro dell'archivio.

$ python3 tarfile_extractfile.py

LEGGIMI.txt :
Gli esempi per il modulo tarfile usano questo file ed esempio.tar come dati.

ERROR: nonqui.txt non trovato nell'archivio tar

Se si vuole estrarre il contenuto dell'archivio e scrivere i file nel filesystem, si usino extract() oppure extractall().

# tarfile_extract.py

import tarfile
import os

os.mkdir('outdir')
with tarfile.open('esempio.tar', 'r') as t:
    t.extract('LEGGIMI.txt', 'outdir')
print(os.listdir('outdir'))

Il membro o i membri sono letti dall'archivio e scritti al filesystem, a partire dalla directory specificata negli argomenti.

$ python3 tarfile_extract.py

Traceback (most recent call last):
  File "tarfile_extract.py", line 6, in <module>
    os.mkdir('outdir')
FileExistsError: [Errno 17] File exists: 'outdir'

La documentazione della libreria standard include una nota che afferma che extractall() è più sicuro di extract(), specialmente lavorando con dati che si stanno ricevendo da un flusso dove rileggere una parte precedente dell'input non è possibile, quindi dovrebbe essere usato nella maggior parte dei casi.

# tarfile_extractall.py

import tarfile
import os

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

Con extractall(), il primo argomento è il nome della directory dove i file dovrebbero essere scritti.

$ python3 tarfile_extractall.py

['LEGGIMI.txt']

Per estrarre specifici file dall'archivio, si passino i loro nomi o dei contenitori di metadati TarInfo ad extractall().

# tarfile_extractall_members.py

import tarfile
import os

os.mkdir('outdir')
with tarfile.open('esempio.tar', 'r') as t:
    t.extractall('outdir',
                 members=[t.getmember('LEGGIMI.txt')],
                 )
print(os.listdir('outdir'))

Quando viene passato un elenco di membri, vengono estratti solo i file inclusi nella lista.

$ python3  tarfile_extractall_members.py

['LEGGIMI.txt']

Creare Nuovi Archivi

Per creare un nuovo archivio, si apra un file tar con TarFile in modalità 'w'.

# tarfile_add.py

import tarfile

print('creazione archivio')
with tarfile.open('tarfile_aggiunto.tar', mode='w') as out:
    print('aggiunto LEGGIMI.txt')
    out.add('LEGGIMI.txt')

print()
print('Contenuto:')
with tarfile.open('tarfile_aggiunto.tar', mode='r') as t:
    for member_info in t.getmembers():
        print(member_info.name)

Un qualsiasi file esistente viene troncato e viene inizializzato un nuovo archivio. Per aggiungere dei file si usi il metodo add().

$ python3 tarfile_add.py

creazione archivio
aggiunto LEGGIMI.txt

Contenuto:
LEGGIMI.txt

Usare Nomi Alternativi per i Membri di un Archivio

E' possibile aggiungere un file a 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().

# tarfile_addfile.py

import tarfile

print('creazione archivio')
with tarfile.open('tarfile_aggiuntofile.tar', mode='w') as out:
    print('aggiunto LEGGIMI.txt come RINOMINATO.txt')
    info = out.gettarinfo('LEGGIMI.txt', arcname='RINOMINATO.txt')
    out.addfile(info)

print()
print('Contenuto:')
with tarfile.open('tarfile_aggiuntofile.tar', mode='r') as t:
    for member_info in t.getmembers():
        print(member_info.name)

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

$ python3 tarfile_addfile.py

creazione archivio
aggiunto LEGGIMI.txt come RINOMINATO.txt

Contenuto:
RINOMINATO.txt

Scrivere Dati da Sorgenti Diverse Dai File

Talvolta è necessario scrivere dati in un archivio direttamente dalla memoria. Invece che scrivere dati a un file, quindi aggiungere quel file all'archivio, si può usare addfile() per aggiungere dati da un handle di tipo file aperto che ritorni byte.

tarfile_aggiungifile_stringa.tar

Per prima cosa si costruisca un oggetto Tarinfo, si può assegnare al membro dell'archivio un qualsiasi nome a scelta. Dopo averne impostata la dimensione, i dati vengono scritti nell'archivio usando addfile(), e passando un buffer BytesIO come sorgente dei dati.

$ python3 tarfile_addfile_string.py

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

Aggiungere agli Archivi

Oltre alla creazione di nuovi archivi, è possibile aggiungere dati a un file esistente usando la modalità 'a'.

# tarfile_append.py

import tarfile

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

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

print('si accoda index.rst')
with tarfile.open('tarfile_accoda.tar', mode='a') as out:
    out.add('index.rst')

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

L'archivio che ne deriva conterrà due membri.

$ python3 tarfile_append.py

creazione archivio
contenuto:
['LEGGIMI.txt']
si accoda 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 o bzip2. Per aprire un archivio compresso, si modifichi la stringa della modalità passata ad open() per includere ":gz" oppure :bz2, a seconda del metodo di compressione desiderato.

# tarfile_compression.py

import tarfile
import os

fmt = '{:<30} {:<10}'
print(fmt.format('NOME FILE', 'DIMENSIONE'))
print(fmt.format('LEGGIMI.txt', os.stat('LEGGIMI.txt').st_size))

FILES = [
    ('tarfile_compressione.tar', 'w'),
    ('tarfile_compressione.tar.gz', 'w:gz'),
    ('tarfile_compressione.tar.bz2', 'w:bz2'),
]

for filename, write_mode in FILES:
    with tarfile.open(filename, mode=write_mode) as out:
        out.add('LEGGIMI.txt')

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

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

$ python3 tarfile_compression.py

NOME FILE                      DIMENSIONE
LEGGIMI.txt                    61        
tarfile_compressione.tar       10240      ['LEGGIMI.txt']
tarfile_compressione.tar.gz    255        ['LEGGIMI.txt']
tarfile_compressione.tar.bz2   238        ['LEGGIMI.txt']

Vedere anche:

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