Automate Boring Tasks with Python: 10 Scripts

Ten automation scripts built only on Python's standard library: rename files with a preview, find duplicates by content, merge monthly CSV exports, clear out stale downloads, archive logs, check links, pull email addresses from text, and schedule the lot to run on its own.

GloryTecks Team

Training & Placement Faculty

6 May 2026 6 min read

Last updated: 24 September 2026

Share:

A task is worth automating when you do it often, it follows the same steps each time, and a mistake in it is annoying rather than catastrophic. Renaming a folder of photos, merging this month's exports, clearing out the downloads folder: each takes ten minutes by hand and ten lines of Python. The ten scripts below use only the standard library, so there is nothing to install.

Every script that changes files follows one rule: print what it would do first, and only act when you pass a flag. A dry run costs nothing and catches the bug that renames all 400 files to the same name.

1. Rename a folder of files, with a preview

Camera and scanner files arrive as IMG_0001.jpg, IMG_0002.jpg and so on. This script gives them a meaningful prefix and a clean sequence number, in name order:

python
from pathlib import Path

def bulk_rename(folder: str, prefix: str, apply: bool = False) -> None:
    files = sorted(Path(folder).glob("IMG_*.jpg"))
    for n, path in enumerate(files, start=1):
        target = path.with_name(f"{prefix}-{n:03d}{path.suffix.lower()}")
        print(f"{path.name} -> {target.name}")
        if apply:
            path.rename(target)

bulk_rename("photos", "2026-09-goa-trip")              # preview only
text
IMG_0001.jpg -> 2026-09-goa-trip-001.jpg
IMG_0002.jpg -> 2026-09-goa-trip-002.jpg
IMG_0003.jpg -> 2026-09-goa-trip-003.jpg

Run it again with apply=True once the preview looks right. sorted() matters: glob returns files in whatever order the file system lists them (Python docs: pathlib).

2. Find duplicate files by content

Names lie; contents do not. Group files by size first (files of different sizes cannot be identical), then hash only the candidates:

python
import hashlib
from collections import defaultdict
from pathlib import Path

def find_duplicates(folder: str) -> list[list[str]]:
    by_size = defaultdict(list)
    for p in Path(folder).rglob("*"):
        if p.is_file():
            by_size[p.stat().st_size].append(p)
    by_hash = defaultdict(list)
    for same_size in by_size.values():
        if len(same_size) < 2:
            continue
        for p in same_size:
            with open(p, "rb") as f:
                by_hash[hashlib.file_digest(f, "sha256").hexdigest()].append(p.name)
    return [sorted(names) for names in by_hash.values() if len(names) > 1]

print(find_duplicates("downloads"))
text
[['report (1).pdf', 'report.pdf']]

hashlib.file_digest() reads the file in chunks, so it works on large files too; it needs Python 3.11 or later (Python docs: hashlib). The script only reports. Deleting is a decision you make after reading the list.

3. Merge monthly CSV exports into one file

Many systems export one CSV per month. Combining them by hand invites copy-paste errors; this script stacks them and records which file each row came from:

python
import csv
from pathlib import Path

def merge_csvs(folder: str, out_file: str) -> int:
    rows = []
    for path in sorted(Path(folder).glob("sales-*.csv")):
        with open(path, newline="", encoding="utf-8") as f:
            for row in csv.DictReader(f):
                rows.append({**row, "source_file": path.name})
    with open(out_file, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=["region", "amount", "source_file"])
        writer.writeheader()
        writer.writerows(rows)
    return len(rows)

print(merge_csvs("exports", "sales-all.csv"), "rows written")
text
4 rows written

Always open CSV files with newline="", as the csv module documentation requires; otherwise quoted fields containing line breaks are misread, and Windows gains blank lines between rows. If the exports might not share the same columns, check each file's header before appending.

4. Clear out files you have not touched in months

python
import time
from pathlib import Path

def stale_files(folder: str, days: int) -> list[str]:
    cutoff = time.time() - days * 86_400
    return sorted(p.name for p in Path(folder).iterdir()
                  if p.is_file() and p.stat().st_mtime < cutoff)

print(stale_files("downloads", 30))
text
['old-installer.exe', 'old-video.mp4']

st_mtime is the last modification time. Review the list, then move the files to a holding folder rather than deleting them outright; empty that folder a month later.

5. Archive last month's logs

python
import shutil

archive = shutil.make_archive("logs-2026-08", "zip", root_dir="logs")
print(archive.endswith("logs-2026-08.zip"))
text
True

make_archive builds a zip (or tar) of a whole folder in one call and returns the archive's path (Python docs: shutil). Delete the originals only after checking the archive opens.

6. Pull every email address out of a pile of text files

python
import re
from pathlib import Path

EMAIL = re.compile(r"[\w.+-]+@[\w-]+(?:\.[\w-]+)+")

found = set()
for p in Path("inbox").glob("*.txt"):
    found.update(m.lower() for m in EMAIL.findall(p.read_text(encoding="utf-8")))
print(sorted(found))
text
['asha@example.com', 'ravi.k@example.co.in', 'sales@example.org']

Lower-casing before adding to the set merges ASHA@example.com with asha@example.com. The pattern is deliberately practical rather than a full implementation of the email address standard. For more patterns, see regular expressions in Python.

python
import urllib.error
import urllib.request

def check(url: str) -> int:
    request = urllib.request.Request(url, method="HEAD", headers={"User-Agent": "link-check"})
    try:
        with urllib.request.urlopen(request, timeout=10) as response:
            return response.status
    except urllib.error.HTTPError as err:
        return err.code

for url in ["https://docs.python.org/3/", "https://docs.python.org/3/no-such-page"]:
    print(check(url), url)

A HEAD request asks for the status without downloading the page. Some servers reject HEAD; if one returns 405, retry that URL with GET. Run it weekly against the links in your documentation or bookmarks, and fix whatever is no longer 200.

8 to 10: report, schedule, log

8. Email yourself the result. Build the message with email.message.EmailMessage, attach the merged CSV, and send it with smtplib.SMTP_SSL and send_message() (Python docs: smtplib). Read the password from an environment variable, never from the script. Most mail providers require an app-specific password for this; see environment variables and secrets in Python apps.

9. Run it on a schedule. On Linux or macOS, one cron line runs a script every Monday at 08:00:

text
0 8 * * 1  /usr/bin/python3 /home/you/scripts/merge_exports.py >> /home/you/scripts/merge.log 2>&1

On Windows, Task Scheduler does the same; from a terminal: schtasks /Create /SC WEEKLY /D MON /ST 08:00 /TN MergeExports /TR "py C:\scripts\merge_exports.py". Use absolute paths, because a scheduled job does not start in your usual folder.

10. Make it tell you what happened. A script that runs unattended needs a log: what it did, what it skipped, and the error if it failed. Replace print with the logging module and write to a file. Python logging: a practical setup shows a configuration that suits small scripts.

When one file out of five hundred is broken

A script that stops at the first bad file leaves the job half done, and one that silently skips it hides a problem. The middle path is to handle each item on its own, carry on, and report at the end:

  • Wrap the work for one file in try/except, catching the specific errors you expect, such as UnicodeDecodeError for a file in the wrong encoding or KeyError for a CSV missing a column.
  • Count successes and failures, and keep the names of the failures.
  • Move files that failed into a needs-attention/ folder instead of deleting or ignoring them.
  • Finish with one summary line, such as "merged 498 files, 2 moved to needs-attention", and exit with a non-zero status if anything failed, so a scheduler can flag the run.

This turns a crash at 3 a.m. into a short list you can deal with over coffee. The same pattern, applied to exceptions in general, is covered in error handling in Python.

Before you automate anything else

  • Keep a copy of the input, at least the first few times the script runs for real.
  • Write down how long the manual task takes and how often you do it. A script that saves ten minutes every week repays an afternoon of writing within a few months; a two-minute chore you do once a year rarely does.
  • Make the script safe to run twice: a second run should do nothing, or the same thing again, never double it.
  • Put the paths and settings at the top of the file, or in arguments, instead of scattered through the code. Building a CLI tool with argparse turns a script like these into a proper command.

Looking for something to build rather than something to automate? The Python projects for beginners list has fifteen ideas. File handling, CSV work and automation scripts are all part of the Python Programming course.

Sources

GloryTecks Team

Training & Placement Faculty

The GloryTecks faculty team mentors learners across Data Science, AI and Data Engineering, with a focus on real-time projects and placement readiness in Hyderabad.

Want to master Python?

Join GloryTecks’ expert-led Python Programming training in Hyderabad with real-time projects and 100% placement support.

Explore course