Automate Windows File Backups with a Python Script and PyInstaller

This guide explains how to create a Python‑based backup utility that automatically copies files or folders on Windows at user‑defined intervals, detailing the script structure, configuration file, core functions, logging, and how to package the tool as a standalone executable with PyInstaller.

21CTO
21CTO
21CTO
Automate Windows File Backups with a Python Script and PyInstaller

Lost or deleted files are common, so regular backups are essential. This article presents a Python script (sync.py) that automatically backs up specified files or folders on Windows, configurable via a Sync1.ini file and compatible with both Python 2 and Python 3.

The program consists of four files: sync.py (main program), Sync1.ini (configuration), logger1.py (logging support), and sync.log (generated log file).

Key steps include importing required libraries, reading the configuration, calculating backup frequency, computing MD5 hashes for files, copying directories and individual files, building dictionaries of original and destination files, comparing them, and copying changed or new files.

import configparser
import time
import shutil
import hashlib
from distutils.dir_util import copy_tree
from collections import OrderedDict
import os
import logger1 as log1

Configuration reading function:

def ConfRead():
    config = configparser.ConfigParser()
    config.read("Sync1.ini")
    return dict(config.items("Para"))

MD5 hash function for a file:

def md5(fname, size=4096):
    hash_md5 = hashlib.md5()
    with open(fname, "rb") as f:
        for chunk in iter(lambda: f.read(size), b""):
            hash_md5.update(chunk)
    return hash_md5.hexdigest()

Utility functions for copying:

def CopyDir(from1, to):
    copy_tree(from1, to)

def CopyFiles(file, path_to_copy):
    shutil.copy(file, path_to_copy)

Function that builds a dictionary of original files (hash, relative path) from the source directory:

def OriginalFiles():
    drive = All_Config.get("from")
    Files_Dict = OrderedDict()
    for root, dirs, files in os.walk(drive, topdown=True):
        for file in files:
            file = file.lower()
            file_path = root + "\\" + file
            try:
                hash1 = md5(file_path, size=4096)
                rel_path = file_path.strip(drive)
                Files_Dict[(hash1, rel_path)] = file_path
            except Exception as e:
                log1.logger.error('Error Original files: {0}'.format(e))
    return Files_Dict

Function that builds a dictionary of destination files, creating the destination folder if it does not exist:

def Destination():
    Files_Dict = OrderedDict()
    from1 = All_Config.get("from")
    to = All_Config.get("to")
    dir1 = from1.rsplit("\\", 1)[1]
    drive = to + dir1
    try:
        if os.path.isdir(drive):
            for root, dirs, files in os.walk(drive, topdown=True):
                for file in files:
                    file = file.lower()
                    file_path = root + "\\" + file
                    try:
                        hash1 = md5(file_path, size=4096)
                        rel_path = file_path.strip(drive)
                        Files_Dict[(hash1, rel_path)] = file_path
                    except Exception as e:
                        log1.logger.error('Error Destination for loop: {0}'.format(e))
            return Files_Dict
        else:
            CopyDir(from1, drive)
            log1.logger.info('Full folder: {0} copied'.format(from1))
            return None
    except Exception as e:
        log1.logger.error('Error Destination: {0}'.format(e))

Comparison logic that identifies new or modified files and copies them to the destination:

def LogicCompare():
    from1 = All_Config.get("from")
    to = All_Config.get("to")
    Dest_dict = Destination()
    if Dest_dict:
        Source_dict = OriginalFiles()
        remaining_files = set(Source_dict.keys()) - set(Dest_dict.keys())
        remaining_files = [Source_dict.get(k) for k in remaining_files]
        for file_path in remaining_files:
            try:
                log1.logger.info('File: {0}'.format(file_path))
                dir, file = file_path.rsplit("\\", 1)
                rel_dir = from1.rsplit("\\", 1)[1]
                rel_dir1 = dir.replace(from1, "")
                dest_dir = to + rel_dir + "\\" + rel_dir1
                if not os.path.isdir(dest_dir):
                    os.makedirs(dest_dir)
                CopyFiles(file_path, dest_dir)
            except Exception as e:
                log1.logger.error('Error LogicCompare: {0}'.format(e))

Main loop that runs the comparison repeatedly according to the configured frequency and total runtime:

i = 0
while True:
    if i >= repeat:
        break
    LogicCompare()
    time.sleep(Freq)
    i = i + 1

Sample Sync1.ini configuration:

[Para]
From = K:\testing1
To = E:\
Freq = 1
Total_time = 5

After creating the script, you can package it into a Windows executable using pyinstaller. The generated dist folder contains the .exe file, which can be placed in C:\Windows for easy command‑line access. The article also shows how to run the program, adjust the INI parameters, and verify the generated Sync.log file.

Backup script illustration
Backup script illustration
Create exe with pyinstaller
Create exe with pyinstaller
Place exe in Windows folder
Place exe in Windows folder
CMD default path
CMD default path
Sync command
Sync command
Copy full folder
Copy full folder
Modified file
Modified file
Executable command
Executable command
Pen drive present output
Pen drive present output
Compiled by 21CTO Community
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

loggingpyinstallerfile-backup
21CTO
Written by

21CTO

21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.