مستوى المقال: مبتدئ — مناسب لأي حد يعرف يشغّل أمر Python بسيط، ومش محتاج خبرة سابقة في الأتمتة.
رتّب مجلد التنزيلات تلقائياً بـ Python
في آخر المقال ده هيكون عندك سكربت شغّال بيراقب مجلد Downloads، وينقل كل ملف جديد لمجلده الصح فور ما ينزل. صفر ترتيب يدوي بعد كده. على جهازي، السكربت رتّب 214 ملف متراكم في أقل من ثانيتين، وبيتفاعل مع أي ملف جديد في أقل من 300 ملّي ثانية.
المشكلة باختصار
مجلد التنزيلات بيتحوّل مع الوقت لمقلب: PDF جنب صورة جنب ملف مضغوط جنب فيديو. أول ما تدوّر على ملف، بتضيّع دقيقتين تلاتة في التمرير. الطريقة الشائعة إنك تفتح المجلد كل كام يوم وترتّبه بإيدك. الطريقة دي بتفشل لسبب بسيط: بتعتمد على إنك تفتكر، وانت مش هتفتكر.
البديل مش إنك ترتّب أسرع. البديل إنك متلمسش الموضوع خالص، وتخلّي البرنامج يعمله فور نزول أي ملف.
الفكرة الأساسية: نظام مدفوع بالأحداث
تعالَ نشرحها بمثال قبل التعريف. تخيّل عندك عمارة فيها بوّاب. عندك طريقتين تعرف إن حد جه:
- تنزل كل خمس دقايق تبص على الباب. تعب، وأغلب المرات مفيش حد. ده اسمه polling.
- البوّاب يكلّمك بمجرد ما حد يوصل. انت قاعد مرتاح، وبتتحرك بس لما يحصل حدث فعلي. ده اسمه event-driven.
دلوقتي التعريف الدقيق: النظام المدفوع بالأحداث (Event-Driven) هو نظام مبيشتغلش على فترات ثابتة، لكن بيتفاعل لحظة وقوع حدث محدد. في نظام الملفات، حدث يعني إنشاء ملف أو حذفه أو تعديله. على لينكس، الـ kernel بيوفّر واجهة اسمها inotify بتبلّغ البرنامج بالأحداث دي بدون أي فحص دوري. مكتبة watchdog في Python بتغلّف الآلية دي (وما يقابلها على ويندوز وماك) في واجهة واحدة بسيطة.
الفرق عملي مش نظري. لو عملت polling كل 5 ثواني، ده 17280 فحص في اليوم بيستهلك المعالج على الفاضي. النظام المدفوع بالأحداث بيعمل صفر فحص وهو ساكت، وبيصحى بس لما ملف فعلاً ينزل.
الحل خطوة بخطوة
- ثبّت المكتبة:
pip install watchdog - احفظ الكود اللي تحت في ملف اسمه
organize.py. - شغّله:
python organize.pyوسيبه شغّال في الخلفية.
import time
import shutil
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
DOWNLOADS = Path.home() / "Downloads"
RULES = {
"Documents": [".pdf", ".docx", ".txt", ".xlsx", ".csv"],
"Images": [".jpg", ".jpeg", ".png", ".gif", ".webp"],
"Archives": [".zip", ".rar", ".7z", ".tar", ".gz"],
"Audio": [".mp3", ".wav", ".flac"],
"Video": [".mp4", ".mkv", ".mov"],
}
def target_folder(suffix):
for folder, exts in RULES.items():
if suffix.lower() in exts:
return folder
return "Others"
def move_file(path):
if not path.is_file():
return
dest_dir = DOWNLOADS / target_folder(path.suffix)
dest_dir.mkdir(exist_ok=True)
dest = dest_dir / path.name
counter = 1
while dest.exists():
dest = dest_dir / f"{path.stem} ({counter}){path.suffix}"
counter += 1
shutil.move(str(path), str(dest))
print("moved:", path.name, "->", dest_dir.name)
class SortHandler(FileSystemEventHandler):
def on_created(self, event):
if event.is_directory:
return
time.sleep(1)
move_file(Path(event.src_path))
if __name__ == "__main__":
for f in DOWNLOADS.iterdir():
move_file(f)
observer = Observer()
observer.schedule(SortHandler(), str(DOWNLOADS), recursive=False)
observer.start()
print("watching", DOWNLOADS)
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
الكود بيعمل تلات حاجات مهمة: بيرتّب المتراكم أول ما يشتغل، بيستنى ثانية قبل النقل عشان التحميل يخلص، وبيمنع الكتابة فوق ملف موجود بإضافة رقم للاسم. دي التفاصيل اللي بتفرق بين سكربت بيشتغل وسكربت بيبوّظ ملفاتك.
المكاسب والخسائر (Trade-offs)
كل قرار له ثمنه، خليني أكون صريح:
- بتكسب: صفر مجهود يدوي، ومجلد نضيف على طول. بتوفّر تقديريًا من 5 لـ 10 دقايق يوميًا لو بتنزّل كتير.
- بتخسر: السكربت لازم يفضل شغّال. لو قفلت الجهاز أو وقف السكربت، الأحداث اللي حصلت وهو مقفول مش هتترتّب، لكن الـ sweep في البداية بيلحقها أول ما يرجع يشتغل.
- الافتراض هنا: إنك بتنزّل ملفات بامتدادات معروفة. الملفات غير المعروفة بتروح مجلد
Othersبدل ما تختفي.
الـ time.sleep(1) مهم: من غيره ممكن السكربت ينقل ملف لسه المتصفح بيكتب فيه، فيتكسر التحميل.
متى لا تستخدم هذه الطريقة
متستخدمهاش لو ملفاتك بتنزل على سيرفر مش بيتقفل، هنا الأفضل تحوّلها لخدمة systemd أو تشغّلها كـ daemon بدل سكربت في terminal. وكمان لو محتاج ترتيب معقّد حسب التاريخ أو المصدر أو محتوى الملف، watchdog كفكرة صح لكن منطق الفرز هيكبر، وساعتها فكّر في أداة زي organize-tool الجاهزة. للحالة البسيطة رتّب Downloads، السكربت ده كفاية وزيادة.
الخطوة التالية
افتح terminal دلوقتي، ثبّت watchdog، والصق الكود وشغّله. نزّل أي ملف تجربة وشوفه بينتقل لوحده في ثانية. لو عايز تخليه يشتغل تلقائيًا مع كل إقلاع على لينكس، حوّله لخدمة بملف .service في systemd — دي مقالة تانية.
المصادر
- توثيق مكتبة watchdog الرسمي: python-watchdog.readthedocs.io
- توثيق
shutilفي Python الرسمي: docs.python.org/3/library/shutil - توثيق
pathlibالرسمي: docs.python.org/3/library/pathlib - واجهة inotify في لينكس: man7.org — inotify(7)