DEV Community

poolion
poolion

Posted on

batch-namer: A Minimal CLI Tool for Batch File Renaming in Python

batch-namer: Simple, Dependency-Free File Renaming

I built batch-namer to solve a common problem: renaming large collections of files without installing heavy dependencies. This lightweight Python script uses only standard library modules to rename files with patterns like "IMG_2024-", UUIDs, timestamps, or sequential numbers.

What Problem Does It Solve?

Organizing photos, scripts, or downloads often means renaming dozens of files at once. Most solutions:

  • Require installation (pip, npm, etc.)
  • Have complex configuration
  • Overcomplicate simple renaming tasks

batch-namer is a single Python script you can copy anywhere and run immediately. It works in shared containers, offline environments, or systems with restricted package managers.

Installation and Usage

Quick Start

# Clone the repo
git clone https://github.com/Poolion/batch-namer.git
cd batch-namer

# Make executable (Linux/Mac)
chmod +x file_renamer.py

# Run with help
python3 file_renamer.py --help
Enter fullscreen mode Exit fullscreen mode

Basic Command Structure

python3 file_renamer.py \
  --source <directory> \
  --dest <output-or-same> \
  --pattern "*.jpg" \
  [rename-options]
Enter fullscreen mode Exit fullscreen mode

Rename with Custom Prefix

Add a date-based prefix to all images:

python3 file_renamer.py \
  --source photos/ \
  --dest photos/ \
  --prefix "IMG_2024-10-30_120000" \
  --pattern "*.jpg"
Enter fullscreen mode Exit fullscreen mode

This renames vacation.jpg to IMG_2024-10-30_120000vacation.jpg.

Rename with UUIDs

Generate unique identifiers for files:

python3 file_renamer.py \
  --source temp/ \
  --dest backup/ \
  --uuid \
  --pattern "*.py"
Enter fullscreen mode Exit fullscreen mode

Each .py file gets a 16-character hex UUID.

Sequential Numbering

Sort documents with numbers:

python3 file_renamer.py \
  --source projects/docs/ \
  --dest docs_sorted/ \
  --number \
  --pattern "*.md"
Enter fullscreen mode Exit fullscreen mode

Files become 1.md, 2.md, 3.md, etc.

Preview with Dry Run

See what would happen before making changes:

python3 file_renamer.py \
  --source photos/ \
  --dest photos/ \
  --pattern "*.jpg" \
  --prefix "BACKUP_" \
  --dry-run
Enter fullscreen mode Exit fullscreen mode

Available Renaming Modes

Option Flag Example Output
Prefix --prefix, -px IMG_2024-10-30_vacation.jpg
UUID --uuid a1b2c3d4e5f67890.jpg
Date --date, -dt 20241030_120500vacation.jpg
Sequence --number 1.jpg, 2.jpg

Date Format

The default date format is %Y%m%d_%H%M%S (e.g., 20241030_120000). Customize with:

python3 file_renamer.py --date "IMG-%Y-%m-%d" --prefix "".jpg"
Enter fullscreen mode Exit fullscreen mode

Safety Features

Dry Run Mode

Before renaming anything, preview changes with --dry-run:

python3 file_renamer.py --source photos/ \
  --dest photos/ \
  --pattern "*.jpg" \
  --prefix "BACKUP_" \
  --dry-run
Enter fullscreen mode Exit fullscreen mode

The tool displays all target renames without touching files.

Conflict Resolution

If a destination filename already exists, batch-namer increments a counter rather than overwriting:

Target: IMG_2024-10-30_vacation.jpg (exists)

IMG_2024-10-30_vacation_1.jpg (new)

This protects against accidental data loss when batch-rename operations encounter duplicates.

Pattern Matching

By default, patterns like "*.jpg" exclude:

  • Hidden files (.bash_profile)
  • System files (.DS_Store, __MACOSX/)
  • Git directories (.git/*)

Use !pattern to explicitly exclude (e.g., "*.jpg" "!*backup*")

Code Implementation Details

The tool's core logic centers on three functions:

Pattern to Regex Conversion

Glob patterns need conversion to regex for filesystem matching:

def pattern_to_regex(pattern: str) -> str:
    # Escape special characters except glob wildcards
    regex = ""
    for char in pattern:
        if char == "*":
            regex += "[^/]*"  # Any characters except /, including none
        elif char == "?":
            regex += "."      # Any single character
        elif char in r"\.^$+{}[]|()<>\\":
            regex += "\\" + char
        else:
            regex += char
    return f"^{regex}$"
Enter fullscreen mode Exit fullscreen mode

This preserves the user's intent (e.g., "*.jpg" matches photo.jpg, but not photos/photo.jpg).

File Discovery

The tool uses Python's built-in glob to expand patterns:

def get_files(patterns, directory):
    path = Path(directory)
    files = []

    for pattern in patterns:
        expanded = path.glob(pattern)
        files.extend(expanded)

    return sorted(files, key=lambda f: f.name.lower())
Enter fullscreen mode Exit fullscreen mode

Files are deduplicated and sorted if a single pattern matches multiple directories with overlapping names.

Safe Renaming Function

Each rename mode uses the same conflict-resolution pattern:

def rename_with_prefix(files, prefix):
    for file_path in files:
        new_name = f"{prefix}{file_path.stem}{file_path.suffix}"
        new_path = file_path.parent / new_name

        # Handle filename conflicts
        counter = 1
        while new_path.exists():
            base, ext = os.path.splitext(file_path.name)
            new_name = f"{prefix}{base}_{counter}.{ext}" if ext else f"{prefix}file_{counter}"
            new_path = file_path.parent / new_name
            counter += 1

        shutil.move(str(file_path), str(new_path))
Enter fullscreen mode Exit fullscreen mode

The while loop keeps incrementing until finding an unused filename, protecting against data loss.

When to Use batch-namer

This tool excels in:

  • Photo organization: Batch-naming downloaded images from cameras
  • Script management: Versioning temporary Python scripts with UUIDs
  • Cleanup operations: Moving "temp/*" files with sequential numbers
  • Portability: Copy the script to any environment without dependencies
  • Offline work: No network or package manager needed

Limitations

  • Python 3.x only (standard library, no external dependencies)
  • Single-threaded (rename one file at a time for safety)
  • Pattern matching via glob, not complex regex customization
  • No GUI interface (it's meant to be headless/terminal-based)

Conclusion

batch-namer demonstrates the power of minimalism: a single script using only Python standard library modules to solve a common problem. If you work with files in bulk and need consistent renaming without dependency hell, give it a try.


Source Code: https://github.com/Poolion/batch-namer

Support Development: https://www.buymeacoffee.com/poolion

Top comments (0)