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:

python
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:

python
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:

text
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:

  1. Baris 5 (sorted(sumber.glob("laporan-*.txt"))): glob() memilih semua berkas yang cocok dengan pola, dan sorted() membuat urutan kerja deterministik sehingga output selalu sama.
  2. Baris 6 (berkas.stem.split("-")[1]): atribut stem berisi nama tanpa akhiran; dipotong tanda - lalu diambil bagian kedua ("2024").
  3. 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:

python
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:

text
struktur siap

Selanjutnya, hitung dan tampilkan semua berkas Python di dalam pohon tersebut:

python
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:

text
proyek/src/main.py
proyek/src/util.py
total .py: 2

Method 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:

python
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:

text
berkas log siap

Pembersihnya, bersihkan_umur.py, menghapus berkas yang lebih tua dari 1 Juni 2025:

python
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:

text
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:

python
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:

text
    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.py

Loop 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:

python
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:

text
[dry-run] akan menghapus: cache.tmp
[dry-run] akan menghapus: draf-sementara.tmp
dihapus: cache.tmp
dihapus: draf-sementara.tmp
sisa .tmp: 0

Kebiasaan 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:

python
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:

text
mode = "produksi"
berkas .tmp tersisa: False

Resep-resep di atas adalah bahan mentah. Chapter penutup section ini, Membangun CLI Profesional, menyatukan pathlib, argparse, logging, dan exit code menjadi satu program utuh.