Working with Files and Folders in Bulk
About 24 minutes
Why this matters
Renaming 200 photos, sorting a messy Downloads folder, finding the biggest files eating your disk: jobs like these take minutes in Python and hours by hand. You already know pathlib for single paths. This lesson adds looping over folders and the shutil module for copying and moving.
The idea
from pathlib import Path
import shutil
folder = Path("photos")
for item in folder.iterdir(): # everything directly inside the folder
if item.is_file():
print(item.name, item.suffix, item.stat().st_size) # name, ".jpg", bytes
folder.rglob("*.jpg") # every .jpg in the folder AND its subfolders
folder.glob("*.jpg") # only directly inside the folder
Useful facts about a path: .name ("cat.jpg"), .stem ("cat"), .suffix (".jpg"), .parent, .is_file(), .is_dir(), .exists(), and .stat().st_size (size in bytes). Compare suffixes with .lower(), because PHOTO.JPG and photo.jpg are the same kind of file.
Copying and moving (the shutil module):
folder.mkdir(parents=True, exist_ok=True) # create it (and parents) if needed
shutil.copy2("a.txt", "backup/a.txt") # copy, keeping the timestamps
shutil.move("a.txt", "archive/a.txt") # move (also renames)
shutil.rmtree("old_folder") # delete a whole folder: careful!
Safety habits for scripts that change files:
- Do a dry run first (print what would happen) before really moving anything.
- Never overwrite silently; check
exists(). - Test on a copy of your data, or in a temporary folder, as the checks here do.
iterdir() gives paths in no particular order, so sorted(...) the results when order matters.
Try it
In a scratch folder, create a few files with different extensions. Loop over the folder with iterdir() and print each file’s name and size.
Your turn
The checks give your functions a temporary folder. Write:
files_by_extension(folder): a dict of lowercase extension (like".txt", or""for none) to the sorted list of file names directly in the folder. Ignore subfolders.big_files(folder, min_bytes): the sorted relative paths (like"sub/big.bin") of every file in the folder and its subfolders that is at leastmin_bytesbig.total_size(folder): the total size in bytes of all files in the folder and its subfolders.copy_matching(src, dst, extension): createdstif needed and copy the files directly insidesrcwith that extension (compared in lowercase) into it. Return the sorted list of copied file names.
Recap
iterdir()lists a folder;glob/rglobmatch patterns (rglobalso searches subfolders).suffix,name,stemandstat().st_sizedescribe a file.shutil.copy2,moveandrmtreecopy, move and delete;mkdir(parents=True, exist_ok=True)creates folders safely.- Dry-run first, never overwrite silently, and test in a temporary folder.
Common mistakes
- Treating folders like files.
iterdir()returns both; checkis_file(). - Case-sensitive extensions:
.JPGdoes not equal.jpguntil you lower-case it. - Running a destructive script on real data first. Print the plan, then act.