tarfile — Acceso a archivos Tar

Propósito:Acceso a archivos Tar

El módulo tarfile proporciona acceso de lectura y escritura a archivos de Unix tar, incluyendo archivos comprimidos. Además de los estándares POSIX, varias extensiones de tar de GNU son compatibles. Tipos especiales de archivos Unix, como los enlaces duros y blandos, y los nodos de dispositivos también son manejados.

Nota

Aunque tarfile implementa un formato Unix, puede usarse también para crear y leer archivos tar en Microsoft Windows.

Archivos Tar de prueba

La función is_tarfile() devuelve un valor booleano que indica si o no, el nombre de archivo pasado como argumento hace referencia a un archivo tar válido.

tarfile_is_tarfile.py
import tarfile

for filename in ['README.txt', 'example.tar',
                 'bad_example.tar', 'notthere.tar']:
    try:
        print('{:>15}  {}'.format(filename, tarfile.is_tarfile(
            filename)))
    except IOError as err:
        print('{:>15}  {}'.format(filename, err))

Si el archivo no existe, is_tarfile() levanta el IOError.

$ python3 tarfile_is_tarfile.py

     README.txt  False
    example.tar  True
bad_example.tar  False
   notthere.tar  [Errno 2] No such file or directory:
'notthere.tar'

Leer de metadatos de un archivo

Usa la clase TarFile para trabajar directamente con un archivo tar. Ésta admite métodos para leer datos sobre archivos existentes, así como modificar los archivos agregando archivos adicionales.

Para leer los nombres de los archivos en un archivo existente, usa getnames().

tarfile_getnames.py
import tarfile

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

El valor de retorno es una lista de cadenas con los nombres del contenido del archivo.

$ python3 tarfile_getnames.py

['index.rst', 'README.txt']

Además de los nombres, los metadatos sobre los miembros del archivo están disponibles como instancias de objetos TarInfo.

tarfile_getmembers.py
import tarfile
import time

with tarfile.open('example.tar', 'r') as t:
    for member_info in t.getmembers():
        print(member_info.name)
        print('  Modified:', time.ctime(member_info.mtime))
        print('  Mode    :', oct(member_info.mode))
        print('  Type    :', member_info.type)
        print('  Size    :', member_info.size, 'bytes')
        print()

Cargue los metadatos a través de getmembers() y getmember().

$ python3 tarfile_getmembers.py

index.rst
  Modified: Fri Aug 19 16:27:54 2016
  Mode    : 0o644
  Type    : b'0'
  Size    : 9878 bytes

README.txt
  Modified: Fri Aug 19 16:27:54 2016
  Mode    : 0o644
  Type    : b'0'
  Size    : 75 bytes

Si el nombre del miembro del archivo es conocido de antemano, su objeto TarInfo se puede recuperar con getmember().

tarfile_getmember.py
import tarfile
import time

with tarfile.open('example.tar', 'r') as t:
    for filename in ['README.txt', 'notthere.txt']:
        try:
            info = t.getmember(filename)
        except KeyError:
            print('ERROR: Did not find {} in tar archive'.format(
                filename))
        else:
            print('{} is {:d} bytes'.format(
                info.name, info.size))

Si el miembro del archivo no está presente, getmember() levanta un KeyError.

$ python3 tarfile_getmember.py

README.txt is 75 bytes
ERROR: Did not find notthere.txt in tar archive

Extraer archivos de un archivo

Para acceder a los datos de un miembro del archivo dentro de un programa, usa el método extractfile(), pasando el nombre del miembro.

tarfile_extractfile.py
import tarfile

with tarfile.open('example.tar', 'r') as t:
    for filename in ['README.txt', 'notthere.txt']:
        try:
            f = t.extractfile(filename)
        except KeyError:
            print('ERROR: Did not find {} in tar archive'.format(
                filename))
        else:
            print(filename, ':')
            print(f.read().decode('utf-8'))

El valor de retorno es un objeto similar a un archivo desde el cual se pueden leer los contenidos del miembro del archivo.

$ python3 tarfile_extractfile.py

README.txt :
The examples for the tarfile module use this file and
example.tar as data.

ERROR: Did not find notthere.txt in tar archive

Para descomprimir el archivo y escribir los archivos en el sistema de archivos, usa extract() o extractall() en su lugar.

tarfile_extract.py
import tarfile
import os

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

El miembro o miembros se leen del archivo y se escriben en el sistema de archivos, comenzando en el directorio nombrado en los argumentos.

$ python3 tarfile_extract.py

['README.txt']

La documentación de la biblioteca estándar incluye una nota que indica que extractall() es más segura que extract(), especialmente para trabajar con la transmisión de datos donde rebobinar para leer una parte anterior de la entrada no es posible, y debe utilizarse en la mayoría de los casos.

tarfile_extractall.py
import tarfile
import os

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

Con extractall(), el primer argumento es el nombre del directorio donde se deben escribir los archivos.

$ python3 tarfile_extractall.py

['README.txt', 'index.rst']

Para extraer archivos específicos del archivo, pasa sus nombres o los contenedores de metadatos TarInfo a extractall().

tarfile_extractall_members.py
import tarfile
import os

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

Cuando se proporciona una lista de members, solo se extraen los archivos nombrados.

$ python3 tarfile_extractall_members.py

['README.txt']

Crear Nuevos Archivos

Para crear un nuevo archivo, abre el TarFile con un modo de 'w'.

tarfile_add.py
import tarfile

print('creating archive')
with tarfile.open('tarfile_add.tar', mode='w') as out:
    print('adding README.txt')
    out.add('README.txt')

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

Cualquier archivo existente se trunca y se inicia un nuevo archivo. Para añadir archivos, usa el método add().

$ python3 tarfile_add.py

creating archive
adding README.txt

Contents:
README.txt

Usar nombres alternativos de miembros de archivo

Es posible agregar un archivo a un archivo usando un nombre diferente al nombre de archivo original mediante la construcción de un objeto TarInfo con una arcname alternativo y pasándolo a addfile().

tarfile_addfile.py
import tarfile

print('creating archive')
with tarfile.open('tarfile_addfile.tar', mode='w') as out:
    print('adding README.txt as RENAMED.txt')
    info = out.gettarinfo('README.txt', arcname='RENAMED.txt')
    out.addfile(info)

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

El archivo incluye solo el nombre de archivo cambiado:

$ python3 tarfile_addfile.py

creating archive
adding README.txt as RENAMED.txt

Contents:
RENAMED.txt

Escribir datos de fuentes que no sean archivos

A veces es necesario escribir datos en un archivo directamente desde la memoria. En lugar de escribir los datos en un archivo, y a continuación, agregar ese archivo al archivo, puedes usar addfile() para agregar datos de un manejador de archivo abierto como un archivo que devuelve bytes.

tarfile_addfile_string.py
import io
import tarfile

text = 'This is the data to write to the archive.'
data = text.encode('utf-8')

with tarfile.open('addfile_string.tar', mode='w') as out:
    info = tarfile.TarInfo('made_up_file.txt')
    info.size = len(data)
    out.addfile(info, io.BytesIO(data))

print('Contents:')
with tarfile.open('addfile_string.tar', mode='r') as t:
    for member_info in t.getmembers():
        print(member_info.name)
        f = t.extractfile(member_info)
        print(f.read().decode('utf-8'))

Al construir primero un objeto TarInfo, le puedes dar cualquier nombre deseado al miembro del archivo. Después de configurar el tamaño, los datos son escritos en el archivo usando addfile() y un buffer BytesIO como fuente de los datos.

$ python3 tarfile_addfile_string.py

Contents:
made_up_file.txt
This is the data to write to the archive.

Anexar a Archivos

Además de crear nuevos archivos, es posible adjuntar a un archivo existente usando el modo 'a'.

tarfile_append.py
import tarfile

print('creating archive')
with tarfile.open('tarfile_append.tar', mode='w') as out:
    out.add('README.txt')

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

print('adding index.rst')
with tarfile.open('tarfile_append.tar', mode='a') as out:
    out.add('index.rst')

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

El archivo resultante termina con dos miembros:

$ python3 tarfile_append.py

creating archive
contents:
['README.txt']
adding index.rst
contents:
['README.txt', 'index.rst']

Trabajar con archivos comprimidos

Además de los archivos regulares de archivos tar, el módulo tarfile puede funcionar con archivos comprimidos a través de los protocolos gzip o bzip2. Para abrir un archivo comprimido, modifica la cadena de modo pasada a open() para que incluya ":gz" o ":bz2", dependiendo del método de compresión deseado.

tarfile_compression.py
import tarfile
import os

fmt = '{:<30} {:<10}'
print(fmt.format('FILENAME', 'SIZE'))
print(fmt.format('README.txt', os.stat('README.txt').st_size))

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

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

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

Al abrir un archivo existente para leer, especifica "r:*" para tener que tarfile determine automáticamente el método de compresión a usar.

$ python3 tarfile_compression.py

FILENAME                       SIZE
README.txt                     75
tarfile_compression.tar        10240      ['README.txt']
tarfile_compression.tar.gz     213        ['README.txt']
tarfile_compression.tar.bz2    199        ['README.txt']

Ver también