Pesquisa de site

Como criar e manipular arquivos tar usando Python


No Linux e em outros sistemas operacionais do tipo Unix, tar é sem dúvida um dos utilitários de arquivamento mais usados; permite-nos criar arquivos, muitas vezes chamados de “tarballs”, que podemos usar para distribuição de código-fonte ou para fins de backup. Neste tutorial veremos como ler, criar e modificar arquivos tar com python, usando o módulo tarfile.

Neste tutorial você aprenderá:

  • Os modos em que um arquivo tar pode ser aberto usando o módulo tarfile

  • O que são as classes TarInfo e TarFile e o que elas representam

  • Como listar o conteúdo de um arquivo tar

  • Como extrair o conteúdo de um arquivo tar

  • Como adicionar arquivos a um arquivo tar

Requisitos de software e convenções usadas

Uso básico

  • O nome do tarball que queremos acessar

  • O modo em que deve ser aberto

O “modo” usado para abrir um arquivo tar depende da ação que queremos realizar e do tipo de compactação (se houver) em uso. Vamos vê-los juntos.

Abrindo um arquivo em modo somente leitura

Se quisermos examinar ou extrair o conteúdo de um arquivo tar, podemos usar um dos seguintes modos, para abri-lo somente leitura:

Mode Meaning
‘r’ Read only mode – the compression type will be automatically handled
‘r:’ Read-only mode without compression
‘r:gz’ Read-only mode – zip compression explicitly specified
‘r:bz2’ Read-only mode – bzip compression explicitly specified
‘r:xz’ Read-only mode – lzma compression explicitly specified

Na maioria dos casos, onde o método de compressão pode ser facilmente detectado, o modo recomendado é ‘r’.

Abrindo um arquivo para anexar arquivos

Se quisermos anexar arquivos a um arquivo existente, podemos usar o modo ‘a’ . É importante notar que só é possível anexar a um arquivo se ele não estiver compactado; se tentarmos abrir um arquivo compactado com este modo, uma exceção ValueError será gerada. Se fizermos referência a um arquivo inexistente, ele será criado instantaneamente.

Abrindo um arquivo para escrever

Se quisermos criar explicitamente um novo arquivo e abri-lo para escrita, podemos usar um dos seguintes modos:

Mode Meaning
‘w’ Open the archive for writing – use no compression
‘w:gz’ Open the archive for writing – use gzip compression
‘w:bz’ Open the archive for writing – use bzip2 compression
‘w:xz’ Open the archive for writing – use lzma compression

Se um arquivo existente for aberto para gravação, ele será truncado e todo o seu conteúdo será descartado. Para evitar tais situações, podemos querer abrir o arquivo exclusivamente, conforme descrito na próxima seção.

Crie um arquivo somente se ele não existir

Quando quisermos ter certeza de que um arquivo existente não será substituído ao criar um arquivo, devemos abri-lo exclusivamente. Se usarmos o modo ‘x’ e já existir um arquivo com o mesmo nome daquele que especificamos para o arquivo, um FileExistsError será gerado. Os métodos de compactação podem ser especificados da seguinte forma:

Mode Meaning
‘x’ Create the archive without compression if doesn’t exist
‘x:gz’ Create the archive with gzip compression only if it doesn’t exist
‘x:bz2’ Create the archive with bzip2 compression only if it doesn’t exist
‘x:xz’ Create the archive with lzma compression only if it doesn’t exist

Trabalhando com arquivos

Existem duas classes fornecidas pelo módulo tarfile que são usadas para interagir com arquivos tar e seu conteúdo, e são, respectivamente: TarFile e TarInfo . O primeiro é usado para representar um arquivo tar em sua totalidade e pode ser usado como um gerenciador de contexto com a instrução Python with, o último é usado para representar um membro do arquivo, e contém várias informações sobre ele. Como primeiro passo, vamos nos concentrar em alguns dos métodos mais usados da classe TarFile: podemos usá-los para realizar operações comuns em arquivos tar.

Recuperando uma lista dos membros do arquivo

Para recuperar uma lista dos membros do arquivo, podemos usar o método getmembers de um objeto TarFile. Este método retorna uma lista de objetos TarInfo, um para cada membro do arquivo. Aqui está um exemplo de seu uso com um arquivo compactado fictício contendo dois arquivos:

>>> with tarfile.open('archive.tar.gz', 'r') as archive:
...     archive.getmembers()
...
[<TarInfo 'file1.txt' at 0x7f58dab50d00>, <TarInfo 'file2.txt' at 0x7f58dab50ac0>]

Como veremos mais adiante, podemos acessar alguns dos atributos de um arquivo arquivado, como sua propriedade e horário de modificação, através das propriedades e métodos do objeto TarInfo correspondentes.

Exibindo o conteúdo de um arquivo tar

Se tudo o que queremos fazer é exibir o conteúdo de um arquivo tar, podemos abri-lo em modo de leitura e usar o método list da classe Tarfile.

>>> with tarfile.open('archive.tar.gz', 'r') as archive:
...     archive.list()
...
?rw-r--r-- egdoc/egdoc          0 2020-05-16 15:45:45 file1.txt
?rw-r--r-- egdoc/egdoc          0 2020-05-16 15:45:45 file2.txt

Como você pode ver, a lista dos arquivos contidos no arquivo é exibida como saída. O método list aceita um parâmetro posicional, verbose  que é True por padrão. Se alterarmos seu valor para False, apenas os nomes dos arquivos serão informados na saída, sem informações adicionais.

O método também aceita um parâmetro nomeado opcional, membros. Se usado, o argumento fornecido deve ser um subconjunto da lista de objetos TarInfo retornados pelo método getmembers. Somente informações sobre os arquivos especificados serão exibidas se esse parâmetro for usado e um valor correto for fornecido.

Extraindo todos os membros do arquivo tar

Outra operação muito comum que podemos querer realizar em um arquivo tar é extrair todo o seu conteúdo. Para realizar tal operação podemos usar o método extractalldo objeto TarFile correspondente. Aqui está o que escreveríamos:

>>> with tarfile.open('archive.tar.gz', 'r') as archive:
...     archive.extractall()

O primeiro parâmetro aceito pelo método é path: ele serve para especificar onde os membros do arquivo devem ser extraídos. O valor padrão é '.', portanto os membros são extraídos no diretório de trabalho atual.

O segundo parâmetro, members, pode ser usado para especificar um subconjunto de membros para extrair do arquivo e, como no caso do método list, deve ser um subconjunto da lista retornada pelo método getmembers.

O método extractall também possui um parâmetro nomeado, numeric_owner. É False por padrão: se mudarmos para True, os números uid e gid serão usados para definir a propriedade dos arquivos extraídos em vez dos nomes de usuários e grupos.

Extraindo apenas um membro do arquivo

E se quisermos extrair apenas um único arquivo do arquivo? Nesse caso queremos usar o método extract e referenciar o arquivo que deve ser extraído pelo seu nome (ou como um objeto TarFile). Por exemplo, para extrair apenas o arquivo file1.txt do tarball, executaríamos:

>>> with tarfile.open('archive.tar.gz', 'r') as archive:
...     archive.extract('file1.txt')

Fácil, não é? O arquivo é extraído no diretório de trabalho atual por padrão, mas uma posição diferente pode ser especificada usando o segundo parâmetro aceito pelo método: caminho.

Normalmente os atributos que o arquivo possui dentro do arquivo são definidos quando ele é extraído no sistema de arquivos; para evitar esse comportamento podemos definir o terceiro parâmetro da função, set_attrs, como False.

O método aceita também o parâmetro numeric_owner: o uso é o mesmo que vimos no contexto do método extractall.

Extraindo um membro do arquivo como um objeto semelhante a um arquivo

Vimos como, usando os métodos extractall e extract, podemos extrair um ou vários membros do arquivo tar para o sistema de arquivos. O módulo tarfile fornece outro método de extração: extractfile. Quando este método é usado, o arquivo especificado não é extraído para o sistema de arquivos; em vez disso, um objeto semelhante a um arquivo somente leitura que o representa é retornado:

>>> with tarfile.open('archive.tar.gz', 'r') as archive:
...     fileobj = archive.extractfile('file1.txt')
...     fileobj.writable()
...     fileobj.read()
...
False
b'hello\nworld\n'

Adicionando arquivos a um arquivo

Até agora vimos como obter informação sobre um arquivo e os seus membros, e os diferentes métodos que podemos utilizar para extrair o seu conteúdo; agora é hora de ver como podemos adicionar novos membros.

A maneira mais fácil que podemos usar para adicionar um arquivo a um arquivo é usando o método add. Referenciamos o arquivo a ser incluído no arquivo por nome, que é o primeiro parâmetro aceito pelo método. O arquivo será arquivado com seu nome original, a menos que especifiquemos um nome alternativo usando o segundo parâmetro posicional: arcname. Suponha que queiramos adicionar o file1.txt a um novo arquivo, mas queremos armazená-lo como archived_file1.txt; escreveríamos:

>>> with tarfile.open('new_archive.tar.gz', 'w') as archive:
...     archive.add('file1.txt', 'archived_file1.txt')
...     archive.list()
...
-rw-r--r-- egdoc/egdoc         12 2020-05-16 17:49:44 archived_file1.txt

No exemplo acima, criamos um novo arquivo descompactado usando o modo 'w' e adicionamos o file1.txt como archive_file1.txt, como você pode ver pela saída de list().

Os diretórios podem ser arquivados da mesma forma: por padrão eles são adicionados recursivamente, ou seja, junto com seu conteúdo. Este comportamento pode ser alterado definindo o terceiro parâmetro posicional aceito pelo método add, recursivo, como False.

E se quisermos aplicar um filtro para que apenas os arquivos especificados sejam incluídos no arquivo? Para este propósito podemos usar o parâmetro opcional filter nomeado. O valor passado para este parâmetro deve ser uma função que recebe um objeto TarInfo como argumento e retorna esse objeto se deve ser incluído no arquivo ou None se deve ser excluído . Vejamos um exemplo. Suponha que temos três arquivos em nosso diretório de trabalho atual: file1.txt, file2.txt e file1.md. Queremos adicionar apenas os arquivos com extensão .txt ao arquivo; aqui está o que poderíamos escrever:

>>> import os
>>> import tarfile
>>> with tarfile.open('new_archive.tar.gz', 'w') as archive:
...     for i in os.listdir():
...         archive.add(i, filter=lambda x: x if x.name.endswith('.txt') else None)
...     archive.list()
...
-rw-r--r-- egdoc/egdoc          0 2020-05-16 18:26:20 file2.txt
-rw-r--r-- egdoc/egdoc          0 2020-05-16 18:22:13 file1.txt

No exemplo acima usamos o método os.listdir para obter uma lista dos arquivos contidos no diretório de trabalho atual. Iterando sobre a lista, usamos o método add para adicionar cada arquivo ao arquivo. Passamos uma função como argumento do parâmetro filter, neste caso uma função anônima, um lambda. A função toma o objeto tarfile como argumento (x) e o retorna se seu nome (nome é uma das propriedades do objeto TarInfo) terminar com “.txt”. Caso contrário, a função retorna None para que o arquivo não seja arquivado.

O objeto TarInfo

Já aprendemos que os objetos TarInfo representam um membro do arquivo tar: ele armazena os atributos do arquivo referenciado e fornece alguns métodos que podem nos ajudar a identificar o próprio tipo de arquivo. O objeto TarInfo não contém os dados reais do arquivo. Alguns dos atributos do objeto TarInfo são:

  • nome (nome do arquivo)

  • tamanho (tamanho do arquivo)

  • mtime (hora de modificação do arquivo)

  • uid (o ID do usuário do proprietário do arquivo)

  • gid (o id do grupo de arquivos)

  • uname (o nome de usuário do proprietário do arquivo)

  • gname (o nome do grupo de arquivos)

O objeto também possui alguns métodos muito úteis, aqui estão alguns deles:

  • isfile() – Retorna True se o arquivo for normal, False caso contrário

  • isdir() – Retorna True se o arquivo for um diretório, False caso contrário

  • issym() – Retorna True se o arquivo for um link simbólico, False caso contrário

  • isblk() – Retorna True se o arquivo for um dispositivo de bloco, False caso contrário

Conclusões

Neste tutorial aprendemos o uso básico do módulo tarfile Python e vimos como podemos usá-lo para trabalhar com arquivos tar. Vimos os vários modos de operação, o que representam as classes TarFile e TarInfo, e alguns dos métodos mais utilizados para listar o conteúdo de um arquivo, para adicionar novos arquivos ou para extraí-los. Para um conhecimento mais aprofundado do módulo tarfile por favor dê uma olhada na documentação oficial do módulo

Artigos relacionados: