#!/usr/bin/env python3 import os import sys import shutil from pathlib import Path import argparse from datetime import datetime def get_new_filename(dest_path: Path, filename: str) -> Path: """Generate a new filename if a collision occurs by adding -01, -02, etc.""" name = Path(filename).stem suffix = Path(filename).suffix counter = 1 new_path = dest_path / filename while new_path.exists(): new_filename = f"{name}-{counter:02d}{suffix}" new_path = dest_path / new_filename counter += 1 return new_path def move_files(source_dir: str, dest_dir: str, extensions: list, dry_run: bool = False) -> tuple: """ Move files with specific extensions from source_dir to dest_dir. Returns tuple of (files_processed, total_size) """ source_path = Path(source_dir).resolve() dest_path = Path(dest_dir).resolve() files_processed = 0 total_size = 0 if not source_path.exists(): print(f"Error: Source directory '{source_dir}' does not exist!") sys.exit(1) # Create destination if it doesn't exist if not dry_run: dest_path.mkdir(parents=True, exist_ok=True) # Convert extensions to lowercase for case-insensitive matching extensions = [ext.lower() for ext in extensions] # Walk through source directory and all subdirectories for root, _, files in os.walk(source_path): for file in files: file_path = Path(root) / file if file_path.suffix.lower() in extensions: files_processed += 1 total_size += file_path.stat().st_size if dry_run: print(f"Would move: {file_path} -> {dest_path / file}") continue # Get new filename if collision occurs new_path = get_new_filename(dest_path, file) # Move file and preserve timestamps shutil.copy2(file_path, new_path) os.remove(file_path) print(f"Moved: {file_path} -> {new_path}") return files_processed, total_size def main(): parser = argparse.ArgumentParser( description="Move files with specific extensions from source directory to destination directory" ) parser.add_argument("source", nargs="?", help="Source directory") parser.add_argument("destination", nargs="?", help="Destination directory") parser.add_argument( "-ext", "--extensions", nargs="+", help="File extensions to move (e.g., .jpg .png .gif)" ) parser.add_argument( "-dry", "--dry-run", action="store_true", help="Perform a dry run without moving files" ) args = parser.parse_args() if not all([args.source, args.destination, args.extensions]): parser.print_help() print("\nExample usage:") print(" move_files.py /source/dir /dest/dir -ext .jpg .png .gif") print(" move_files.py /source/dir /dest/dir -ext .jpg -dry") sys.exit(1) # Ensure extensions start with dot extensions = [ext if ext.startswith('.') else f'.{ext}' for ext in args.extensions] files_processed, total_size = move_files( args.source, args.destination, extensions, args.dry_run ) # Print summary action = "Would move" if args.dry_run else "Moved" print(f"\nSummary:") print(f"Files {action}: {files_processed}") print(f"Total size: {total_size / 1024 / 1024:.2f} MB") if __name__ == "__main__": main()