Scripts e automação de arquivos

Crie um backup ZIP com data e verifique o que foi arquivado

Crie um arquivo ZIP com data a partir de uma pasta sem alterar os originais e verifique se todos os arquivos de origem aparecem no arquivo com conteúdo correspondente. Uma pequena pasta sintética facilita a conferência manual dos caminhos, tamanhos e totais esperados.

Ver o sumário

A tradução foi feita com IA. Confira o código, as unidades e os valores junto com o original. A revisão por falantes nativos de cada idioma ainda não foi concluída. English

Para quem éEste guia é voltado a pessoas que querem um backup simples de pasta com data e uma etapa explícita de verificação, em vez de presumir que a criação do arquivo foi bem-sucedida.

Preparação
  • Python 3.12 e um comando de terminal que inicie essa versão.
  • Uma pasta de trabalho em que os scripts possam criar pastas dentro de outputs.
  • Permissão de leitura para os arquivos de origem e espaço em disco suficiente para o arquivo ZIP.
  • É necessária apenas a biblioteca padrão do Python: datetime, hashlib, pathlib e zipfile.

01Defina o que o backup deve conter

O script arquivará arquivos regulares de uma pasta de origem, incluindo arquivos em subpastas aninhadas. Os caminhos dentro do ZIP são armazenados em relação à pasta de origem, portanto um arquivo como reports/week1.txt permanece reports/week1.txt em vez de se tornar um caminho absoluto do sistema de arquivos.

O nome do arquivo contém a data atual do calendário local no formato YYYY-MM-DD. Se o exemplo for executado em 2026-09-20, o nome esperado será backup_demo_2026-09-20.zip. Executá-lo novamente no mesmo dia com o mesmo destino faz o script parar em vez de substituir o backup anterior.

02Crie uma pasta de origem sintética

O conjunto de dados a seguir é sintético e foi criado especificamente para este artigo. Salve o script de preparação como create_backup_demo.py. Ele cria quatro arquivos com conteúdo exato em bytes, incluindo um arquivo em uma pasta reports aninhada.

python
from pathlib import Path

SOURCE = Path("outputs") / "backup_demo"
FILES = {
    "notes.txt": b"alpha\n",
    "reports/week1.txt": b"beta\n",
    "reports/week2.txt": b"gamma\n",
    "settings.ini": b"mode=test\n",
}

SOURCE.parent.mkdir(parents=True, exist_ok=True)
SOURCE.mkdir()  # Stop if the synthetic source already exists.

for relative_name, content in FILES.items():
    target = SOURCE / relative_name
    target.parent.mkdir(parents=True, exist_ok=True)
    with target.open("xb") as stream:
        stream.write(content)
text
python create_backup_demo.py
Caminho relativoConteúdo exatoBytes
notes.txtalpha mais quebra de linha6
reports/week1.txtbeta mais quebra de linha5
reports/week2.txtgamma mais quebra de linha6
settings.inimode=test mais quebra de linha10

A origem contém 4 arquivos regulares totalizando 27 bytes de conteúdo descompactado: 6 + 5 + 6 + 10. O tamanho final do próprio arquivo ZIP não pode ser previsto a partir desse total porque a compactação ZIP e os metadados do arquivo adicionam sua própria sobrecarga.

03Prepare a execução do backup com data

  1. Salve o script principal como dated_zip_backup.py ao lado do script de preparação.
  2. Mantenha SOURCE como outputs/backup_demo neste exemplo.
  3. Mantenha BACKUP_DIR como outputs/backups para que o arquivo fique fora da pasta de origem.
  4. Execute o script a partir do mesmo diretório de trabalho.
text
python dated_zip_backup.py

O script verifica se a origem existe e se o diretório de backup não está dentro do diretório de origem. A pasta pai do backup pode já existir. O próprio arquivo ZIP com data não pode existir.

04Crie o ZIP e verifique cada membro

O script primeiro cria uma lista ordenada dos arquivos de origem. Ele cria o arquivo usando criação exclusiva, grava cada arquivo com compactação ZIP_DEFLATED e depois reabre o arquivo para verificação. A verificação compara a lista de membros pretendida, testa a integridade CRC com testzip e compara os hashes SHA-256 dos arquivos de origem e arquivados.

python
from datetime import date
import hashlib
from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile

SOURCE = Path("outputs") / "backup_demo"
BACKUP_DIR = Path("outputs") / "backups"
CHUNK_SIZE = 1024 * 1024


def hash_file(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as stream:
        while chunk := stream.read(CHUNK_SIZE):
            digest.update(chunk)
    return digest.hexdigest()


def hash_member(archive: ZipFile, member_name: str) -> str:
    digest = hashlib.sha256()
    with archive.open(member_name, "r") as stream:
        while chunk := stream.read(CHUNK_SIZE):
            digest.update(chunk)
    return digest.hexdigest()


def main() -> None:
    source_root = SOURCE.resolve(strict=True)
    if not source_root.is_dir():
        raise ValueError("SOURCE must be a directory.")

    backup_root = BACKUP_DIR.resolve()
    if backup_root.is_relative_to(source_root):
        raise ValueError("BACKUP_DIR must be outside SOURCE.")

    files = sorted(
        path for path in source_root.rglob("*")
        if path.is_file() and not path.is_symlink()
    )
    if not files:
        raise ValueError("SOURCE contains no regular files to back up.")

    BACKUP_DIR.mkdir(parents=True, exist_ok=True)
    archive_name = f"{source_root.name}_{date.today().isoformat()}.zip"
    archive_path = BACKUP_DIR / archive_name
    if archive_path.exists():
        raise FileExistsError(f"Backup already exists: {archive_path}")

    expected = {}
    with archive_path.open("xb") as raw:
        with ZipFile(raw, "w", compression=ZIP_DEFLATED) as archive:
            for path in files:
                relative = path.relative_to(source_root).as_posix()
                expected[relative] = (path.stat().st_size, hash_file(path))
                archive.write(path, arcname=relative)

    with ZipFile(archive_path, "r") as archive:
        bad_member = archive.testzip()
        if bad_member is not None:
            raise RuntimeError(f"CRC check failed: {bad_member}")

        members = [info for info in archive.infolist() if not info.is_dir()]
        actual_names = [info.filename for info in members]
        expected_names = list(expected)
        if actual_names != expected_names:
            raise RuntimeError("Archive member list does not match the source list.")

        for info in members:
            expected_size, expected_hash = expected[info.filename]
            if info.file_size != expected_size:
                raise RuntimeError(f"Size mismatch: {info.filename}")
            if hash_member(archive, info.filename) != expected_hash:
                raise RuntimeError(f"Hash mismatch: {info.filename}")

    total_bytes = sum(size for size, _ in expected.values())
    print(f"Verified {len(expected)} files totaling {total_bytes} bytes.")
    print(f"Archive: {archive_path.as_posix()}")


if __name__ == "__main__":
    main()

O código ignora links simbólicos e arquiva apenas arquivos regulares. Os hashes esperados são calculados a partir da origem imediatamente antes de cada arquivo ser adicionado. Para uma pasta local normal que permaneça inalterada durante a execução, isso fornece uma comparação útil em nível de conteúdo após a criação do arquivo.

05Verifique manualmente o arquivo esperado

Se o script for executado em 2026-09-20, o caminho esperado do arquivo será outputs/backups/backup_demo_2026-09-20.zip. O ZIP deve conter exatamente os quatro caminhos de membros abaixo.

Membro do arquivoBytes descompactados
notes.txt6
reports/week1.txt5
reports/week2.txt6
settings.ini10

A pasta reports aninhada é representada pelos próprios caminhos dos membros. O script não precisa de uma entrada de diretório separada para reports porque os arquivos já carregam esse caminho relativo.

A saída esperada do console abaixo foi derivada manualmente para uma execução em 2026-09-20. Não é um log capturado de execução.

text
Verified 4 files totaling 27 bytes.
Archive: outputs/backups/backup_demo_2026-09-20.zip

06Faça algumas verificações independentes

  1. Abra o ZIP em um visualizador de arquivos compactados e confirme que há exatamente 4 arquivos com os caminhos relativos esperados.
  2. Extraia o arquivo para um local temporário separado e compare o conteúdo de texto com os 4 arquivos de origem.
  3. Confirme que os arquivos originais ainda existem em outputs/backup_demo depois que o backup for concluído.
  4. Execute novamente o script de backup na mesma data sem alterar o destino. Ele deve parar com FileExistsError em vez de substituir o arquivo existente.
  5. Se quiser confirmar que a verificação acompanha o novo conteúdo, altere uma cópia de um arquivo de origem em um teste separado e crie um novo backup com um nome de arquivo diferente.

Não use o tamanho do arquivo compactado como verificação principal. A compactação significa que um arquivo válido pode ser menor ou maior do que o total descompactado da origem, dependendo dos dados e dos metadados.

07Reconheça erros comuns

SintomaO que verificar
FileNotFoundErrorVerifique SOURCE e o diretório de trabalho do terminal. Neste exemplo, execute primeiro o script de preparação sintético.
FileExistsErrorJá existe um backup com o mesmo nome datado. Revise-o e escolha outro destino ou aguarde outra data de backup pretendida em vez de sobrescrevê-lo.
PermissionErrorConfirme que o script consegue ler todos os arquivos de origem e criar arquivos dentro de BACKUP_DIR.
CRC check failedTrate o arquivo como falho. Não dependa dele como backup; investigue erros de armazenamento ou gravação e crie um novo arquivo.
Member list mismatchA origem pode ter sido alterada durante a execução, ou o conteúdo do arquivo pode não corresponder à lista de arquivos montada no início.
Hash or size mismatchTrate a verificação como falha. Mantenha a origem original inalterada e crie um novo arquivo após investigar a divergência.

Se ocorrer uma exceção depois que o arquivo ZIP tiver sido criado, um arquivo parcial ou não verificado pode permanecer na pasta de backups. O script não o exclui automaticamente porque uma limpeza automática pode ocultar evidências úteis para diagnosticar a falha.

08Entenda o que este backup não garante

Este é um fluxo de arquivamento por cópia de arquivos, não um snapshot transacional do sistema de arquivos. Se outro aplicativo modificar um arquivo enquanto ele estiver sendo calculado por hash ou arquivado, o resultado pode ficar inconsistente. Para dados importantes, interrompa as gravações na origem ou use um sistema de backup com suporte a snapshots.

O exemplo não preserva todas as propriedades do sistema de arquivos. Permissões, propriedade, listas de controle de acesso, atributos estendidos, fluxos de dados alternativos, links simbólicos e metadados específicos de aplicativos podem exigir outra ferramenta de backup. ZIP é útil para coleções portáteis de arquivos, mas não é uma imagem completa de um sistema de arquivos.

Um arquivo verificado ainda é apenas uma cópia. Um plano real de backup deve considerar múltiplas cópias, falhas de armazenamento, exclusão acidental, ransomware, períodos de retenção, criptografia e se a recuperação foi realmente testada. A verificação aqui mostra que este arquivo correspondeu ao conteúdo da origem observado durante esta execução; ela não estabelece uma estratégia completa de recuperação de desastres.

Registro de execução e verificação

2026-09-20 · exemplo verificado manualmente · alvo: Python 3.12 · biblioteca padrão: datetime, hashlib, pathlib, zipfile · sem execução

  • Foram verificados manualmente os tamanhos dos quatro arquivos sintéticos como 6, 5, 6 e 10 bytes.
  • Foi calculado manualmente o total descompactado da origem como 27 bytes.
  • Foram listados manualmente os caminhos esperados dos membros do arquivo, incluindo os dois caminhos aninhados de reports.
  • O código foi inspecionado para confirmar que os arquivos de origem são lidos, o ZIP é gravado dentro de outputs e um arquivo datado já existente faz a execução parar.
  • A lógica de verificação foi inspecionada quanto ao teste CRC, comparação da lista de membros, comparação do tamanho descompactado e comparação SHA-256.
  • O nome esperado do arquivo para 2026-09-20 e a saída do console foram derivados manualmente.
Limites da verificação
  • O código não foi executado pelo autor desta resposta; nenhum arquivo ZIP foi criado nem aberto.
  • Os valores SHA-256 e os resultados CRC não foram calculados manualmente porque a lógica de comparação é a verificação relevante para este exemplo.
  • Alterações simultâneas na origem, falhas de permissão, corrupção de armazenamento, gravações interrompidas, links simbólicos e desempenho em diretórios grandes não foram testados.
  • O tamanho compactado do arquivo não foi previsto porque depende do resultado da compactação e dos metadados ZIP.
  • As URLs da documentação oficial foram fornecidas com base em locais de documentação conhecidos, mas não foram verificadas ao vivo.

Princípios de redação e verificação de todo o site

Fontes de referência

As explicações e os exemplos são de elaboração própria. Os comportamentos e conceitos relacionados podem ser consultados nas fontes oficiais abaixo.