Libro Python Aplicado de Eugenia Bahit. GNU/Linux, ciencia de datos, y desarrollo web

Banner de Python Aplicado

Bibliotecas para manejo avanzado de archivos en GNU/Linux


Cita con formato IEEE:
E. Bahit, "Bibliotecas para manejo avanzado de archivos en GNU/Linux", in Python Aplicado, 4th ed., EBRC Publisher, 2022, pp. 140-145.

Cita con formato APA 7:
Bahit, E. (2022). Bibliotecas para manejo avanzado de archivos en GNU/Linux. In Python Aplicado (4th ed., pp. 140-145). EBRC Publisher.

Cita en línea:
(Bahit, 2022)

En este capítulo:

Compresión y descompresión de archivos con ltarfile y zipfile

La biblioteca tarfile puede utilizarse para leer, comprimir y descomprimir archivos .tar, .tar.gz. tar.bz2 y tar.xz, mientras que la biblioteca zipfile se utiliza para los archivos .zip

La biblioteca tarfile

Bien sea para leer un archivo comprimido, o bien para comprimir o descomprimir, un objeto TarFile, se crea mediante la función open del módulo. A diferencia de una apertura estándar, los modos lectura y escritura, se acompañan del formato deseado, mediante la sintaxis <modo>:<formato>, donde <modo> puede ser r (lectura) o w (escritura), y <formto>, gz (gzip), bz2 (bzip2) o, solo en Python 3, xz (lzma).

Modos de apertura y equivalencias con el comando tar.
Modo de apertura Comando tar
[r|w]:gz tar -[c|x]z
[r|w]:bz2 tar -[c|x]j
[r|w]:xz tar -[c|x]J
El formato lzma (xz) solo está disponible a partir de la rama 3 del lenguaje

Descomprimir archivos

from tarfile import open as tar_open
      
with tar_open("origen.tar.bz2", "r:bz2") as tar:
    tar.extractall('carpeta/destino')

Comprimir archivos

from tarfile import open as tar_open
      
with tar_open("carpeta/destino.tar.gz", "w:gz") as tar:
    tar.add('foo.txt')
    tar.add('bar.txt')
    tar.add('baz.txt')

Observaciones generales sobre el código:

Se utiliza un alias para que el método open de la biblioteca tarfile, no sobrescriba la función incorporada open. Se emplea la estructura with, para no utilizar el método close.

Observaciones de seguridad:

No deben descomprimirse archivos sin verificar el nombre de los mismos. Un nombre de archivo, podría contener una / o .., que provocarían que los archivos se almacenasen en un directorio no esperado.

Observaciones de compatibilidad entre versiones:

El formato lzma solo está disponible en la rama 3 del lenguaje. Para que un script o herramienta sea compatible con ambas versiones, la única opción es utilizar los formatos gzip o bzip2.

La biblioteca zipfile

La extracción y compresión de archivos zip se realiza de la siguiente forma:

from zipfile import ZipFile
      
# Escritura de archivos zip
with ZipFile('carpeta/destino.zip', 'w') as z:
    z.write('foo.txt')
    z.write('bar.txt')
    z.write('baz.txt')
      
# Lectura de archivos zip
with ZipFile('carpeta/origen.zip', 'r') as z:
    z.extractall('carpeta/destino', pwd='clave')
    # el parámetro pwd (contraseña) es opcional
        

Para los archivos zip, aplican las mismas observaciones de seguridad que para los archivos tar.

Manejo de archivos temporales con la biblioteca tempfile

Cuando sea necesario que un script, guarde temporalmente archivos, no es una buena práctica que el mismo script los guarde y luego los elimine, ni que intente escribir directamente en el directorio /tmp. Para este caso, se debe emplear la biblioteca tempfile.

Lectoescritura de archivos temporales

Cuando se crean objetos de archivos temporales mediante la clase TemporaryFile del módulo tempfile, los mismos se crean y destruyen en tiempo de ejecución. La destrucción se lleva a cabo al cerrar el archivo. Esto implica que si se trabaja con la estructura with, al finalizar dicha estructura, el archivo se habrá eliminado.

from tempfile import TemporaryFile
      
with TemporaryFile() as tmp:
    # aquí el archivo existe
      
# Aquí el archivo ya no existe

Todo archivo temporal escrito, para ser escrito, requiere que el contenido se pase como un objeto tipo bytes (y no una cadena). Este requerimiento es exigencia de Python 3, sin embargo, en Python 2 está perfectamente soportado. Para que una cadena sea convertida a bytes, basta con especificar su tipo:

from tempfile import TemporaryFile
      
with TemporaryFile() as tmp:
    tmp.write(b"Cadena de texto que será pasada a bytes")

Finalmente, se debe tener en cuenta que una vez escrito, el cursor estará al final del archivo, por lo que si se lo quiere leer, retornará una cadena nula. Por lo tanto, habrá que mover el cursor al byte 0 a fin de poder leerlo:

from tempfile import TemporaryFile
      
with TemporaryFile() as tmp:
    tmp.write(b"Cadena de texto que será pasada a bytes")
    # … acciones intermedias
    tmp.seek(0) # Se mueve el cursor al byte 0
    contenido = tmp.read()
Observaciones importantes: es necesario aclarar que los archivos temporales creados con, no son archivos persistentes en memoria, sino en disco. De hecho, se almacenan en el directorio temporal del sistema, independientemente de la plataforma. Es posible conocer este directorio invocando a la función gettempdir():
from tempfile import TemporaryFile, gettempdir

with TemporaryFile() as tmp:
    tmp.write(b"Cadena de texto")
    tmp_dir = gettempdir()

Búsqueda de archivos con las bibliotecas glob y fnmatch

Estas bibliotecas permiten buscar archivos que coincidan con un patrón, con el mismo estilo empleado en sistemas *nix. Mientras que el módulo glob busca archivos que coincidan con un patrón, fnmatch verifica si un patrón coincide con el nombre de un archivo.

Símbolos interpretados

Símbolos interpretados por glob y fnmatch en la búsqueda de archivos
Símbolo Significado
* Cualquier coincidencia
? Coincidencia con un único carácter
[secuencia] Coincidencia con cualquier carácter de la secuencia
[!secuencia] Coincidencia con cualquier carácter, excepto los de la secuencia

Uso de glob

>>> from glob import glob
>>> glob('*.txt')
['foo.txt', 'baz.txt', 'bar.txt']
>>> glob('*[!of].txt')
['baz.txt', 'bar.txt']

Uso de fnmatch con os.listdir

>>> from os import listdir
>>> from fnmatch import fnmatch
>>> for archivo in listdir('.'):
...   archivo, fnmatch(archivo, '*[!0-9].txt')
...

('3.r', False)
('foo.gif', False)
('.bar', False)
('carpeta', False)
('foo.txt', True)
('baz.txt', True)
('origen.tar.xz', False)
('.foo', False)
('2.r', False)
('bar.txt', True)
('.baz', False)
('origen.tar.bz2', False)
('a.r', False)
('1.r', False)
('origen.tar.gz', False)