Mengganti nama dua puluh berkas, menghapus log lama, dan meringkas isi folder pekerjaan adalah pekerjaan yang memboroskan waktu manusia dan mudah salah. Python dengan pathlib mengerjakannya dengan cepat, terulang, dan dapat diaudit. Chapter ini mengumpulkan resep otomasi file system yang membangun di atas materi Chapter Bekerja dengan pathlib dan Mengelola Direktori: rename massal berpola, pembersihan berdasarkan umur, laporan ukuran, serta dua kebiasaan profesional — dry-run dan penulisan atomik.
Rename Massal dengan Pola #
Siapkan panggung uji berupa folder unduhan:
from pathlib import Path
unduhan = Path("unduhan")
unduhan.mkdir(exist_ok=True)
for nama in ("laporan-2024.txt", "laporan-2025.txt", "foto-liburan.png", "catatan.txt"):
(unduhan / nama).write_text("isi berkas\n")Isi folder tersebut sekarang: catatan.txt, foto-liburan.png, laporan-2024.txt, laporan-2025.txt. Tujuan kita: membalik nama laporan-YYYY.txt menjadi YYYY-laporan.txt agar terurut berdasarkan tahun. Berkas rename_massal.py:
from pathlib import Path
sumber = Path("unduhan")
for berkas in sorted(sumber.glob("laporan-*.txt")):
tahun = berkas.stem.split("-")[1]
nama_baru = f"{tahun}-laporan.txt"
berkas.rename(sumber / nama_baru)
print(f"{berkas.name} -> {nama_baru}")
print("sesudah:", sorted(p.name for p in sumber.iterdir()))Output yang Dihasilkan:
laporan-2024.txt -> 2024-laporan.txt
laporan-2025.txt -> 2025-laporan.txt
sesudah: ['2024-laporan.txt', '2025-laporan.txt', 'catatan.txt', 'foto-liburan.png']Mari kita bedah baris-baris pentingnya:
- Baris 5 (
sorted(sumber.glob("laporan-*.txt"))):glob()memilih semua berkas yang cocok dengan pola, dansorted()membuat urutan kerja deterministik sehingga output selalu sama. - Baris 6 (
berkas.stem.split("-")[1]): atributstemberisi nama tanpa akhiran; dipotong tanda-lalu diambil bagian kedua ("2024"). - Baris 8 (
berkas.rename(sumber / nama_baru)):rename()memindahkan path ke nama baru pada folder yang sama.
Menjelusuri Pohon Direktori dengan rglob #
glob() mencari satu tingkat; rglob() turun ke semua subfolder secara rekursif. Siapkan folder proyek bertingkat dengan ukuran berkas yang beragam:
from pathlib import Path
proyek = Path("proyek")
(proyek / "src").mkdir(parents=True, exist_ok=True)
(proyek / "dokumen").mkdir(exist_ok=True)
(proyek / "src" / "main.py").write_text("print('halo')\n")
(proyek / "src" / "util.py").write_text("PI = 3.14\n")
(proyek / "dokumen" / "catatan.txt").write_text("c" * 250)
(proyek / "dokumen" / "laporan.txt").write_text("x" * 5000)
(proyek / "README.txt").write_text("z" * 2000000)
print("struktur siap")Output yang Dihasilkan:
struktur siapSelanjutnya, hitung dan tampilkan semua berkas Python di dalam pohon tersebut:
from pathlib import Path
proyek = Path("proyek")
for berkas in sorted(proyek.rglob("*.py")):
print(berkas.as_posix())
print("total .py:", len(list(proyek.rglob('*.py'))))Output yang Dihasilkan:
proyek/src/main.py
proyek/src/util.py
total .py: 2Method as_posix() mencetak path dengan pemisah / di semua sistem operasi, sehingga output konsisten. Untuk pohon yang besar, bungkus rglob dengan generator atau sum(1 for _ in ...) agar tidak menampung seluruh list di memory.
Pembersihan Berdasarkan Umur Berkas #
Setiap berkas menyimpan waktu modifikasi terakhir pada atribut stat().st_mtime dalam bentuk detik epoch. Bandingkan angka itu dengan batas waktu, dan pembersih berkas tua tinggal satu percabangan. Siapkan folder logs berisi dua berkas dengan umur berbeda — os.utime() menyetel log-lama.log ke 1 Januari 2024 dan log-baru.log ke 10 Juni 2025:
import os
from pathlib import Path
logs = Path("logs")
logs.mkdir(exist_ok=True)
(logs / "log-lama.log").write_text("log 2024\n")
(logs / "log-baru.log").write_text("log baru\n")
os.utime(logs / "log-lama.log", (1704067200, 1704067200))
os.utime(logs / "log-baru.log", (1749494400, 1749494400))
print("berkas log siap")Output yang Dihasilkan:
berkas log siapPembersihnya, bersihkan_umur.py, menghapus berkas yang lebih tua dari 1 Juni 2025:
import time
from pathlib import Path
folder_log = Path("logs")
batas_waktu = time.mktime((2025, 6, 1, 0, 0, 0, 0, 0, -1))
for berkas in sorted(folder_log.glob("*.log")):
if berkas.stat().st_mtime < batas_waktu:
berkas.unlink()
print("dihapus :", berkas.name)
else:
print("disimpan:", berkas.name)
print("sisa:", sorted(p.name for p in folder_log.iterdir()))Output yang Dihasilkan:
disimpan: log-baru.log
dihapus : log-lama.log
sisa: ['log-baru.log']Fungsi time.mktime() mengubah waktu kalender menjadi angka epoch agar dapat dibandingkan dengan st_mtime; argumen terakhirnya adalah penanda DST (-1 berarti biarkan sistem menentukan).
Laporan Ukuran dalam Format Manusiawi #
Angka st_size bersatuan byte tidak ramah dibaca. Fungsi kecil berikut memilih satuan yang tepat lalu berjalan menelusuri pohon proyek:
from pathlib import Path
def format_ukuran(jumlah_byte):
nilai = float(jumlah_byte)
for satuan in ("B", "KB", "MB"):
if nilai < 1024 or satuan == "MB":
return f"{nilai:.1f} {satuan}"
nilai /= 1024
def laporan(folder):
for berkas in sorted(folder.rglob("*")):
if berkas.is_file():
ukuran = format_ukuran(berkas.stat().st_size)
print(f"{ukuran:>10} {berkas.as_posix()}")
laporan(Path("proyek"))Output yang Dihasilkan:
1.9 MB proyek/README.txt
250.0 B proyek/dokumen/catatan.txt
4.9 KB proyek/dokumen/laporan.txt
14.0 B proyek/src/main.py
10.0 B proyek/src/util.pyLoop pada format_ukuran() membagi nilai dengan 1024 sampai jatuh pada satuan yang cocok, sementara {:>10} merata-kanankan kolom angka agar rapi.
dry-run dan Penulisan yang Aman #
Menghapus dan memindahkan berkas tidak bisa di-undo dari Python. Kebiasaan profesionalnya: mode dry-run yang menampilkan rencana tanpa menjalankannya, lalu jalankan sungguhan setelah rencana disetujui:
from pathlib import Path
def bersihkan_tmp(folder, dry_run=True):
"""Hapus semua berkas .tmp; saat dry_run hanya tampilkan rencana."""
for berkas in sorted(folder.glob("*.tmp")):
if dry_run:
print(f"[dry-run] akan menghapus: {berkas.name}")
else:
berkas.unlink()
print(f"dihapus: {berkas.name}")
unduhan = Path("unduhan")
(unduhan / "draf-sementara.tmp").write_text("draf")
(unduhan / "cache.tmp").write_text("cache")
bersihkan_tmp(unduhan, dry_run=True)
bersihkan_tmp(unduhan, dry_run=False)
print("sisa .tmp:", len(list(unduhan.glob("*.tmp"))))Output yang Dihasilkan:
[dry-run] akan menghapus: cache.tmp
[dry-run] akan menghapus: draf-sementara.tmp
dihapus: cache.tmp
dihapus: draf-sementara.tmp
sisa .tmp: 0Kebiasaan kedua: menulis berkas secara atomik. Tulis dulu ke berkas sementara di folder yang sama, lalu pindahkan ke tujuan dengan replace() yang mengganti berkas lama sekaligus — pembaca tidak pernah melihat berkas setengah jadi:
from pathlib import Path
def tulis_atomik(path, isi):
sementara = path.with_name(path.name + ".tmp")
sementara.write_text(isi)
sementara.replace(path)
pengaturan = Path("pengaturan.toml")
tulis_atomik(pengaturan, 'mode = "produksi"\n')
print(pengaturan.read_text(), end="")
print("berkas .tmp tersisa:", pengaturan.with_name(pengaturan.name + ".tmp").exists())Output yang Dihasilkan:
mode = "produksi"
berkas .tmp tersisa: FalseResep-resep di atas adalah bahan mentah. Chapter penutup section ini, Membangun CLI Profesional, menyatukan pathlib, argparse, logging, dan exit code menjadi satu program utuh.