برنامج بلغة بايثون لتنظيم الملفات.

كيف تُبرمج سكريبت بايثون يقوم بتنظيم ملفات مُجلد ما بدلا عنك؟.

شاهينمنذ سنتين

المقدمة:

لربما لديك مجلد تضع به كل أنواع الملفات من فيديوهات و صور إلى ملفات مضغوطة و غيرها، و كل مرة تخبر نفسك أنك ستقوم بتنظيمه لكن لما ترى تلك الفوضى تتهرب و تُبقيه على ذاك الحال، حسنا أتفق معك في هذا خاصة و لو كان مجلدك به أكثر من 50 ملف، ستتعب فقط بمجرد التفكير في أنك ستُضطر للقيام بالكثير من البحث و النسخ و اللصق.

في هذا المقال:

لحل هذه المشكلة، سنقوم بكتابة برنامج بايثون يقوم بهذا العمل المٌمل بدلا عنا و كل ما يحتاجه هذا البرنامج منك هو مسار المُجلد المُراد تنظيمه، كما أنه بانتهائك من قراءة ( وتطبيق ) هذا المقال ستأخذ نظرة لا بأس بها عن كيفية التعامل مع الملفات عن طريق بايثون.

بيان نِطاق المشروع:

البرنامج سيقوم بتنظيم ملفات مجلد معين، و تصنيفها حسب 6 تصنيفات ( Documents, Compressed, Videos, Programs, Music, Images ) البرنامج سيقوم بالتالي:

  • تحديد التصنيفات.

  • الحصول على مسار المجلد المراد تنظيمه إما عن طريق وسائط سطر الأوامر، أو من الحافِظة.

  • التحقق من أن مسار المجلد صحيح.

  • التحقق إذا كان هناك مجلدات للتصنيفات المحددة، إذا لم يكن كذلك البرنامج سيقوم بإنشاء مجلدات للتصنيفات.

  • الحصول على قائمة بها جميع الملفات الموجودة بالملف.

  • المرور على جميع الملفات و نقل كل ملف حسب إمتداده إلى التصنيف الذي ينتمي له.

كتابة البرنامج:

قبل أن نبدأ بكتابة الكود البرمجي، سأخبرك أن هذا المقال ليس موجه لتعلم أساسيات بايثون، لذلك ينبغي أن تكون على معرفة ( و لو بسيطة ) بأساسيات اللغة من حلقات و جمل شرطية و غيرها.

سنحتاج كذلك لوحدة - pyperclip - هذا الأخير سيساعدنا في التعامل مع الحافظة - clipboard - ( كل شيء تقوم بنسخه - Copy - يُخزن في ما يُسمى بالحافظة )، لتثبيت هذه الوحدة أدخل السطر التالي في الطرفية أو cmd ( حسب نظام التشغيل الذي تستعمله ):

pip install pyperclip

و الآن لنبدأ على بركة الله.

الخطوة الأولى:

إفتح مُحرر الأكواد الخاص بك، و أنشئ ملف بايثون جديد و سمه كيف تشاء، أنا سميته files_manager.py، و اكتب فيه ما يلي:

# files_manager.py - Orgnize files of the given folder.

import sys, os, shutil, pyperclip

سنحتاج إلى 4 وحدات - Modules - و هي:

  1. وحدة sys: للتعامل مع وسائط سطر الأوامر - command line arguments -.

  2. وحدة os: للتعامل مع المسارات - paths -.

  3. وحدة shutil: للتعامل مع الملفات ( نقل الملفات ).

  4. وحدة pyperclip: للتعامل مع الحافظة - clipboard -

الخطوة الثانية:

لتحديد التصنيفات سننشئ قائمة من نوع tuple - قائمة ثابتة الحجم - و نضع بها التصنفيات التي نرغب بها:

# مجلدات التصنيفات.
CLASSES = ('Documents', 'Compressed', 'Programs', 'Videos', 'Music','Images')
DOCUMENTS, COMPRESSED, PROGRAMS, VIDEOS, MUSIC, IMAGES =  CLASSES

في السطر الثاني نقوم بإنشاء متغيرات ستُسهِّل علينا عملية نقل الملفات حسب التصنيفات، و نُعرِّف كل متغير بقيمة من قائمة التصنيفات حسب الترتيب، اي كما لو أننا نقوم بالأمر التالي لكن بكتابة أقل:

# هذا الكود يقوم بنفس الأمر السابق.
CLASSES = ('Documents', 'Compressed', 'Programs', 'Videos', 'Music','Images')
DOCUMENTS = CLASSES[0]
COMPRESSED = CLASSES[1]
PROGRAMS = CLASSES[2]
VIDEOS = CLASSES[3]
MUSIC = CLASSES[4]
IMAGES = CLASSES[5]

الخطوة الثالثة:

الآن يلزمنا الحصول على مسار المجلد المُراد تنظيمه، و يوجد طريقتين:

إما عن طريق وسائط سطر الأوامر، و ما أقصده بهذا هو لما تريد مثلا تشغيل هذا البرنامج الحالي ستقوم بتشغيله من المحرر الخاص بك أو عن طريق سطر الأوامر هكذا:

python files_manager.py <some_arguments>

في مكان some_arguments سيكون مسار المُجلد:

python files_manager.py D:\MyFolder

أو عن طريق الذهاب للمجلد و نسخ مساره و ترك البرنامج يتكفل بالمسار المنسوخ باستخدام وحدة pyperclip.

للتكفل بكلا من الطريقتين أضف الأسطر الآتية للسكريبت:

# الحصول على مسار المُجلد.
if len(sys.argv) > 1:
    folder_path = sys.argv[1]
else:
    folder_path = pyperclip.paste()
folder_path = os.path.abspath(folder_path)

في الوحدة sys يوجد متغير argv إختصار لـ' Arguments Vector ' و هي قائمة بها وسائط سطر الأوامر، لذلك نرى إن كان حجم القائمة أكبر من 1 فهذا يعني أنه تم تمرير مسار المجلد ضمن الوسائط عند تشغيل البرنامج و للتوضيح أكثر المُتغير argv سيحمل أحد القيمتين التاليتين:

- في حال تمرير مسار المجلد عند تشغيل البرنامج:

# قيمة المتغير
# argv
# إذا مررنا وسائط سطر الأوامر للبرنامج.
['files_manager.py', 'D:\MyFolder']

كما ترى العنصر الأول هو إسم البرنامج، و العنصر الثاني هو مسار المُجلد، و لهذا نقوم بتعريف متغير ( folder_path ) و نُعطيه قيمة العنصر الثاني حتى نحتفظ بالمسار.

- في حال لم يتم تمرير مسار المجلد ضمن الوسائط :

# قيمة المتغير 
# argv
# إذا لمم نمرر وسائط سطر الأوامر للبرنامج. 
['files_manager.py']

في هذه الحالة حجم القائمة ليس أكبر من 1 ما يعني أننا شَغَّلنا البرنامج بدون وسائط الأوامر، و لهذا نفترض أننا قُمنا بنسخ مسار المجلد فنستعين بالوحدة pyperclip و نستدعي الدالة ()past التي تُلصق ما هو مُخزن في الحافظة للمتغير ( folder_path )، فمثلا لو قمت بنسخ المسار التالي D:\MyFolder الدالة ()past ستُلصق هذه القيمة في المتغير ( folder_path ) و بالتالي قيمة هذا الأخير ستكون:

# قيمة المتغير
# folder_path
# إذا نسخت المسار 
# D:\MyFolder
'D:\MyFolder'

في السطر الأخير نقوم بجعل المسار، مسارا كاملا - Absolute Path - و لفهم هذه النقطة يلزم أن تعرف أنه في المسارات يوجد نوعان:

- المسارات الكاملة - Absolute Path - ( أو المُطلقة ) مثل: D:\MyFolder أو C:\User\Shaheen تُعرف بهذا الإسم لأنها تُعطي مسارا كاملا لمكان وجود الملف أو المجلد.

- المسارات النسبية - Relative Paths - و هذه تعتمد على رمزين هما . و .. الأول معناه ' المُجلد الحالي ' و الثاني معناه ' المجلد الأب ' فمثلا يمكننا تشغيل البرنامج الخاص بنا و إعطائه مسارا من هذا الشكل:

python files_manager.py .\MyFolder

المسار MyFolder\. يعني ' ضمن المجلد الحالي يوجد مجلد بإسم MyFolder ' كما يمكن تقديم مسار من هذا الشكل:

python files_manager.py ..\MyFolder

المسار MyFolder\.. يعني ' ضمن المجلد الأب للمجلد الحالي يوجد مجلد بإسم MyFolder '.

و لهذا نستعمل دالة ()abspath للتحويل من المسارات النسبية إلى المسارات الكاملة، و ذلك لأن المسارات النسبية قد يكون فيها نسبة أخطاء أكبر.

الخطوة الرابعة:

قبل البدأ في العمل مع الملفات ينبغي التحقق من أن المسار المُقدم مسار صحيح و ليس به أخطاء، لذلك أضف ما يلي:

# Check folder path validity.
if not os.path.exists(folder_path):
    print('Invalid folder path.')
    sys.exit(1)

نستعمل الدالة ()exists التي تتأكد من صِحة المسار و تُرجع إما True أو False لذلك إن لم يكن المسار صحيحا نطبع جملة ' .Invalid folder path '، و نُلغي تشغيل البرنامج بالدالة ()exit و نمرر لها الرقم 1 ( يمكن إستدعاء الدالة دون تمرير شيء لكن تمرير رقم ( عدا الـ0 ) يعني أنه حدث خطأ أثناء تشغيل البرنامج ).

الخطوة الخامسة:

في هذه الخطوة سنتحقق من وجود مُجلد لكل من التصنيفات التي حددناها سابقا، إن لم يكن هناك مجلد لتصنيف ما سنقوم بإنشائه:

# التحقق من توفر مجلد لكل من التصنيفات الحالية.
for c in CLASSES:
    class_folder = os.path.join(folder_path, c)
    if os.path.exists(class_folder):
        print(c + ' Folder found.')
    else:
        print('Making directory for %s' % c)
        os.mkdir(class_folder)

نمُر على عناصر قائمة التصنيفات، و نُنشئ متغير بإسم class_folder الذي سيحمل مسار مجلد التصنيف فمثلا من أجل تصنيف Documents قيمة المتغير ستكون:

# قيمة المتغير 
# class_folder
# في حالة ما إذا كان التصنيف الحالي هو
# Documents.
'D:\MyFolder\Documents'

قد تتساءل لماذا نستعمل الدالة ()join ولا نقوم بالأمر هكذا:

class_folder = folder_path + '\\' + c

هذا السطر سيُنتج نفس المسار السابق من أجل تصنيف Documents، لكن هذا صحيح فقط من أجل نظام Windows ماذا عن مستخدمي أنظمة GNU/Linux؟ في توزيعات لينكس مسار الملفات يستخدم الرمز ' / ' بدلا من الرمز ' \ ' و هذا ما سيؤدي بحدوث خطأ في برنامجنا بالنسبة لأنظمة GNU/Linux، و لهذا نستعين بالدالة ()join التي ستتكفل بالرمز المُستخدم في المسارات.

ملاحظة: كما تعلم الرمز ' \ ' في بايثون يُعتبر رمز هروب ( escape charachter ) حيث يستخدم لطباعة ( أو إضافتها لمتغير نصي ) بعض الرموز، مثلا لو أردت طباعة الرمز ' إما أن تضعه بين " هكذا

" ' "

أو تستخدم رمز الهروب ( لن يتم طباعة رمز الهروب ):

'\''

غير هذا، سحصل خطأ في برنامجك، و لذلك الرمز ' \ ' لديه خاصية معينة في البايثون، و لتحديد أنك تريد الرمز نفسه و ليس رمز الهروب يجب أن تضيف رمز هروب قبله ( حتى تحدد أنك تريد طباعة الرمز نفسه و ليس خاصية الهروب ):

'\\'

الخطوة السادسة:

و الآن نحتاج لقائمة بها جميع الملفات الموجودة بالمجلد، حتى يمكننا نقلها حسب إمتدادها ( نوعها ):

# قائمة بها جميع الملفات الموجودة بالمجلد.
files = os.listdir(folder_path)

نمرر مسار المجلد للدالة ()listdir و هذه الأخيرة ستُرجع قائمة بها أسماء كل الملفات الموجودة في المجلد.

الخطوة السابعة و الأخيرة:

ما علينا الآن سوى المرور على كل الملفات و تحديد نوع كل ملف، و نقل الملف حسب نوعه إلى التصنيف الذي ينتمي له:

# المرور على كل الملفات و نقل كل ملف إلى التصنيف الذي ينتمي له.
for file in files:
    file_path = os.path.join(folder_path, file)
    
    if os.path.isdir(file_path):
        continue
    
    if file.endswith('.pdf') or file.endswith('.txt') or file.endswith('.odt') or file.endswith('.docx'):
        print('Moving %s to %s...' % (file, DOCUMENTS))
        #shutil.move(file_path, os.path.join(folder_path, DOCUMENTS))
    elif file.endswith('.zip') or file.endswith('.rar') or file.endswith('.tar'): 
        print('Moving %s to %s...' % (file, COMPRESSED))
        #shutil.move(file_path, os.path.join(folder_path, COMPRESSED))
    elif file.endswith('.deb') or file.endswith('.rpm') or file.endswith('.exe') or file.endswith('.AppImage'):
        print('Moving %s to %s...' % (file, PROGRAMS))
        #shutil.move(file_path, os.path.join(folder_path, PROGRAMS))
    elif file.endswith('.mp4') or file.endswith('.mkv'):
        print('Moving %s to %s...' % (file, VIDEOS))
        #shutil.move(file_path, os.path.join(folder_path, VIDEOS))
    elif file.endswith('.mp3'):
        print('Moving %s to %s...' % (file, MUSIC))
        #shutil.move(file_path, os.path.join(folder_path, MUSIC))
    elif file.endswith('.png') or file.endswith('.jpg') or file.endswith('.jpeg') or file.endswith('.svg'):
        print('Moving %s to %s...' % (file, IMAGES))
        #shutil.move(file_path, os.path.join(folder_path, IMAGES))
print('Done!')

جزء كبير أليس كذلك؟ لكن ما يقوم به أمر بسيط جدا، في السطر الأول ( بعد الحلقة ) نقوم بتحديد مسار الملف باستخدام الدالة ()join ثم بعدها نتحقق إذا ما كان هذا الملف مجلد، إذا كان كذلك نتجاوز ما تبقى من الكود و نمُر للملف التالي.

إذا لم يكن كذلك نتحقق من إمتداد الملف الحالي ( الإمتدادات هي التي تحدد نوع الملف ملا الملفات النصية تنهتي بها إمتداد txt. ) و للتحقق من الإمتداد نستعين بالدالة ()endswith التي نمرر لها الإمتداد و ستحقق ما إذا كان الملف ينتهي بهذا الإمتداد أو لا.

إذا كان الملف به ينتهي بالإمتداد الذي حددناه نطبع الجملة ' ...Moving %s to %s ' حيث s% الأول سيحمل الإسم الملف و الثاني إسم التصنيف الذي سيُنقل له الملف، كما يمكن أن ترى أنني علقت السطر الذي به ()shutil.move هذا السطر هو من يقوم بعملية نقل الملفات، و قم بتعليقه حتى نُشَّغل البرنامج على الأقل مرة واحدة و نتأكد من أنه يعمل بشكل جيد دون أخطاء، عن طريق رؤية ما يتم طباعته لنا في سطر الأوامر بعدها يمكننا إعادة السطر للعمل و تشغيل البرنامج مرة أخرى.

في الأخير و بعد الإنتهاء من المرور على جميع الملفات نطبع الجملة ' !Done '.

أتمنى أن تكونوا قد استفدتم، نلتقي في مقال آخر إن شاء الله.

للإطلاع على الشيفرة المصدرية النهائية للبرنامج: Source Code

صورة الملفات التي في الصورة من موقع Storyset

كلمات دليلية: python script بايثون
3
إعجاب
1515
مشاهدات
0
مشاركة
3
متابع

التعليقات (1)

محمد الصفطي:

أريد أن يتم تنفيذ الأوامر على مجلد سيتم انشاء ملفات فيه مستقبلا ما هي الاكواد 
مجلد به ملفات واريد ترتيب الملفات به ترتيبا ابجديا على الشاشة .

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !