دليل شامل: سرد الملفات في دليل باستخدام Python – مقارنة بين listdir و system(“ls”)
مقدمة إلى عالم إدارة الملفات في بايثون
مرحباً بك أيها المطور الطموح! إذا كنت تسعى لفهم آليات عمل الدوال الأساسية في بايثون التي تتعامل مع نظام الملفات، وكيفية استغلالها بكامل طاقتها، فهذا المقال هو دليلك الشامل. سنغوص معاً في أعماق المفاهيم الجوهرية اللازمة للعمل بفعالية مع الدالتين listdir() و system()، وهما أداتان قويتان لإدارة الملفات والدلائل.
سنبدأ رحلتنا بفهم:
- وحدة
osالمدمجة في بايثون وكيفية استيرادها. - مفهومي "الدليل" و "الدليل التشغيلي الحالي".
- كيفية التحقق من دليلك التشغيلي الحالي وتغييره.
- الفرق بين المسار المطلق والمسار النسبي.
بعد ذلك، سنتعمق في الدوال نفسها:
- كيفية العمل مع دالة
listdir()ومتى يجب استخدامها. - كيفية العمل مع دالة
system("ls")ومتى يجب استخدامها. - أمثلة عملية لكلا الدالتين وشرح لكيفية عملهما خلف الكواليس.
لنبدأ!
وحدة os: جسر بايثون إلى نظام التشغيل
الدالتان الرئيسيتان اللتان سنتناولهما في هذا المقال، وهما listdir() و system()، تنتميان إلى وحدة os في بايثون. هذه الوحدة الغنية بالوظائف مصممة خصيصاً للتفاعل مع نظام التشغيل الخاص بك، مما يتيح لك تنفيذ مجموعة واسعة من الإجراءات مثل:
- إنشاء دليل جديد.
- إعادة تسمية دليل موجود.
- إزالة دليل.
- عرض المسار إلى دليلك التشغيلي الحالي.
- والمزيد الكثير!
نصيحة سريعة:
- الدليل (
directory) هو ما نعرفه عادةً باسم "المجلد" (folder)، حيث نقوم بتخزين الملفات ذات الصلة و/أو الدلائل الأخرى، مما يخلق تسلسلاً هرمياً من الدلائل داخل الدلائل التي تسمى الدلائل الفرعية (subdirectories). مثال على الدليل هو مجلد "المستندات" الخاص بك. - الوحدة (
module) هي ملف يحتوي على تعليمات برمجية (Python code) ذات صلة.
كيفية استيراد وحدة os
لاستخدام وحدة os في برنامجك النصي (script)، تحتاج إلى "استيرادها" (import). استيراد الوحدة يعني الحصول على إمكانية الوصول إلى جميع الدوال والمتغيرات المخزنة بداخلها. نقوم باستيراد الوحدة عندما نرغب في استخدام تعليماتها البرمجية في برنامجنا النصي.
لاستيراد وحدة os، ما عليك سوى تضمين هذا السطر في أعلى برنامج بايثون النصي الخاص بك أو تشغيله في بيئة بايثون التفاعلية (interactive shell):
import os
سيمنحك هذا الوصول إلى جميع الدوال المعرفة في وحدة os.
نصيحة: تم تثبيت هذه الوحدة بالفعل عند تثبيت بايثون 3، لذا ستتمكن من استخدامها على الفور.
لتتمكن من استخدام الدوال من وحدة os، ستحتاج إلى إضافة البادئة os. قبل اسم الدالة التي تريد استدعاءها، هكذا:
os.<function>(<params>)
على سبيل المثال:
os.mkdir( "New Folder" )
كيفية استيراد دوال فردية
إذا كنت ستعمل مع دالة واحدة أو اثنتين فقط من الوحدة، يمكنك استيرادها بشكل فردي باستخدام هذا التركيب:
from <module> import <function1>, <function2>, ...
على سبيل المثال:
from os import listdir, system
في هذه الحالة، يمكنك استدعاء الدوال في برنامجك النصي كالمعتاد، دون إضافة البادئة os.، هكذا:
<function>(<params>)
على سبيل المثال:
mkdir( "New Folder" )
الدليل التشغيلي الحالي (Current Working Directory)
الآن دعنا نرى مفهوماً مهماً جداً تحتاج إلى معرفته قبل البدء في العمل مع listdir() و system(). دليلك التشغيلي الحالي (Current Working Directory)، كما يوحي الاسم، هو الدليل (المجلد) الذي تعمل فيه حالياً.
يمكنك التحقق من دليلك التشغيلي الحالي باستخدام هذه الدالة من وحدة os:
os.getcwd()
ستعرض لك هذه الدالة المسار إلى دليلك التشغيلي الحالي.
نصيحة: cwd تعني "الدليل التشغيلي الحالي" (current working directory).
من بيئة بايثون التفاعلية (Interactive Shell)
إذا قمت بتشغيل هذا الأمر في بيئة بايثون التفاعلية (على نظام Windows)، فسأرى هذا:
>>> os.getcwd()
'C:\Users\estef\AppData\Local\Programs\Python\Python38-32'
هذا هو المسار الكامل لدليلي التشغيلي الحالي:
'C:\Users\estef\AppData\Local\Programs\Python\Python38-32'
من برنامج نصي (Script)
إذا قمت بتشغيل هذا الأمر من برنامج نصي، هكذا:
import os
print(os.getcwd())
فسأرى:
C:\Users\estef\Documents\freeCodeCamp\freeCodeCamp News\listdir vs system
هذا هو المسار الكامل للبرنامج النصي (موقعه في النظام، في التسلسل الهرمي للدلائل).
نصيحة: إذا قمت بتشغيل برنامج نصي (ملف بايثون)، فإن دليلك التشغيلي الحالي هو الدليل الذي يتواجد فيه البرنامج النصي حالياً.
كيفية تغيير دليلك التشغيلي الحالي
يمكنك تغيير دليلك التشغيلي الحالي باستخدام هذا الأمر من وحدة os:
os.chdir(<path>)
ستحتاج إلى تحديد المسار إلى الدليل التشغيلي الجديد، وتمريره كوسيط (argument)، بتنسيق سلسلة نصية (string). يمكن أن يكون إما مساراً مطلقاً أو مساراً نسبياً.
نصيحة:
- المسار المطلق (
Absolute Path) يحدد جميع تسلسلات الدلائل التي تحتاج إلى المرور بها للوصول إلى دليلك المستهدف. يبدأ هذا المسار من الدليل الجذري لنظامك. على سبيل المثال:
>>> import os
>>> os.chdir( r"C:\Users\estef\Documents\FreeCodeCamp\freeCodeCamp News\9 - listdir vs system" )
# Checking current working directory:
>>> os.getcwd()
'C:\Users\estef\Documents\FreeCodeCamp\freeCodeCamp News\9 - listdir vs system'
لاحظ أنني أضفت حرف r قبل المسار المطلق لتحويل السلسلة إلى سلسلة خام (raw string). إذا استخدمت \ ولم تضف r، فستحصل على خطأ لأن الرمز \ سيعامل كحرف خاص (special character). بدلاً من ذلك، يمكنك استبدال الشرطات المائلة العكسية \ بالشرطات المائلة الأمامية / في المسار:
>>> os.chdir( "C:/Users/estef/Documents/FreeCodeCamp/freeCodeCamp News/9 - listdir vs system" )
# Checking current working directory
>>> os.getcwd()
'C:\Users\estef\Documents\FreeCodeCamp\freeCodeCamp News\9 - listdir vs system'
- المسار النسبي (
Relative Path) يحدد المسار الذي تريد اتباعه للعثور على الدليل المستهدف، ولكن الآن يبدأ المسار من دليلك التشغيلي الحالي. إنه أقصر وأبسط من المسار المطلق. على سبيل المثال، إذا كان دليلك التشغيلي الحالي يحتوي على دليل فرعي (مجلد)Directory 1، يمكنك الانتقال إلى هذا الدليل باستخدام مسار نسبي (تخيله كمجلد داخل مجلد آخر، ونحن نتعمق أكثر وأكثر في التسلسل الهرمي)، هكذا:
>>> import os
>>> os.chdir( ".\Directory 1" )
# Check current working directory
>>> os.getcwd()
'C:\Users\estef\Documents\FreeCodeCamp\freeCodeCamp News\9 - listdir vs system\Directory 1'
نصيحة: النقطة (.) في بداية المسار النسبي .\Directory 1 تمثل الدليل التشغيلي الحالي. تُستخدم النقطتان المزدوجتان (..) للانتقال إلى الأعلى في التسلسل الهرمي، إلى الدليل "الأب".
الآن بعد أن أصبح لديك كل المعرفة الأساسية التي ستحتاجها لفهم كيفية عمل listdir() و system() حقاً، دعنا نراهما بالتفصيل.
دالة listdir(): استكشاف محتويات الدلائل برمجياً
سنبدأ بدالة listdir(). دعنا نكشف أسرارها.
الغرض والقيمة المرجعة
وفقاً لوثائق بايثون، الغرض من هذه الدالة هو:
إرجاع قائمة تحتوي على أسماء الإدخالات في الدليل المحدد بواسطة المسار.
بشكل أساسي، تُرجع هذه الدالة قائمة بأسماء جميع الملفات والدلائل الموجودة حالياً داخل دليل معين تحدده عند استدعاء الدالة.
نصيحة: القائمة لن تكون بترتيب معين، حتى لو كنت تقوم عادةً بفرز العناصر أبجدياً.
التركيب والوسيط (Parameter)
لاستدعاء listdir()، ستحتاج إلى استخدام هذا التركيب:

الوسيط path هو بالضبط ذلك، المسار المطلق أو النسبي للدليل الذي تريد عرضه. في بايثون 3.2 والإصدارات الأحدث، هذا الوسيط اختياري. افتراضياً، سيؤدي المسار إلى دليلك التشغيلي الحالي إذا لم تمرر وسيطاً.

تذكر أنه يجب عليك استيراد وحدة os قبل استدعاء هذه الدالة.
نصيحة: إذا استخدمت عبارة الاستيراد from os import listdir لاستيراد الدالة بشكل فردي، يمكنك حذف البادئة os.، هكذا:

حالات الاستخدام والمزايا
تُعد دالة listdir() مفيدة جداً لأنها تعمل على أي نظام تشغيل يعمل عليه بايثون، لذا إذا تم تثبيت بايثون على جهازك، فستعمل هذه الدالة بشكل صحيح.
الآن دعنا نتحدث قليلاً عن قيمتها المرجعة. بما أنها تُرجع قائمة، يمكننا تخزين هذه القائمة في متغير والعمل بها في برنامجنا. على سبيل المثال، لنفترض أننا نريد فعل شيء ما بجميع الملفات من دليل معين، مثل تحويل الصور إلى أبيض وأسود أو تعديل محتواها. يمكننا القيام بذلك باستخدام حلقة تكرارية (for loop)، هكذا:
images = os.listdir(<path>)
for image in images:
# Do something to the image
بالطبع، ستحتاج إلى تعريف ما يحدث داخل الحلقة، لكن هذا مثال على ما يمكنك فعله بهذه الدالة. هذا رائع، أليس كذلك؟
ولكن وجود الملفات والدلائل في نفس القائمة يمكن أن يكون مشكلة صغيرة إذا أردنا العمل مع حلقة تكرارية، أليس كذلك؟ سنحتاج إلى إضافة شرط للتحقق من نوع كل عنصر. كيف يمكننا إنشاء قائمة تحتوي فقط على أسماء الملفات (بدون دلائل) أو العكس؟ دعنا نرى!
تضمين الملفات فقط
إذا كنت ترغب في "تصفية" القائمة المرجعة بواسطة os.listdir() لتضمين الملفات فقط (بدون دلائل)، يمكنك استخدام سطر التعليمات البرمجية هذا:
list(filter(os.path.isfile, os.listdir(<path>)))
نصيحة: يمكنك تخصيص الوسيط <path> أو حذفه لاستخدام دليلك التشغيلي الحالي.
دعنا نرى مثالاً مع دليلي التشغيلي الحالي (أنا أستخدم Windows):

يحتوي دليلي (المجلد) على:
- دليلين فرعيين (مجلدات داخل المجلد الرئيسي)
- ملف PowerPoint واحد
- ملف صورة واحد (ملف
.png) - برنامج بايثون نصي واحد
إذا استدعيت دالة listdir() من ملف script.py وطبعت القائمة المرجعة:
print(os.listdir())
هذا هو الإخراج:
[ 'Diagrams.ppt' , 'Directory 1' , 'Directory 2' , 'listdir vs system.png' , 'script.py' ]
يمكنك أن ترى أن جميع الملفات والدلائل من دليلي التشغيلي الحالي تم تضمينها.
لتصفية القائمة لتضمين الملفات فقط، يمكننا استخدام هذه العبارة:
print(list(filter(os.path.isfile, os.listdir())))
الآن الإخراج هو:
[ 'Diagrams.ppt' , 'listdir vs system.png' , 'script.py' ]
لاحظ كيف تم "تصفية" الدلائل، وهذا بالضبط ما أردناه.
تضمين الدلائل فقط
وبالمثل، إذا كنت ترغب في "تصفية" القائمة لتضمين الدلائل فقط، يمكنك استخدام سطر التعليمات البرمجية هذا:
list(filter(os.path.isdir, os.listdir(<path>)))
الآن الإخراج هو:
[ 'Directory 1' , 'Directory 2' ]
بالضبط ما أردناه. ولكن كيف تعمل هذه العبارة خلف الكواليس؟ دعنا نرى.
كيف تعمل دالة filter() خلف الكواليس
تُستدعى دالة filter() باستخدام هذا التركيب:
filter(<function>, <list>)
إنها تقوم أساساً "بتصفية" عناصر الوسيط الثاني (القائمة) بناءً على القيمة المنطقية (truth value) المرجعة عن طريق استدعاء الدالة الممررة كوسيط أول (os.path.isfile() أو os.path.isdir() في أوامرهما الخاصة):
print(list(filter(os.path.isfile, os.listdir())))
list(filter(os.path.isdir, os.listdir()))
هاتان الدالتان:
os.path.isfile(<path>)
os.path.isdir(<path>)
تُرجعان True إذا كان الوسيط ملفاً أو دليلاً، على التوالي. بناءً على هذه القيم المنطقية، سيتم تضمين عناصر القائمة (أو عدم تضمينها) في القائمة "المصفاة" النهائية.
يتم تمرير عناصر القائمة المرجعة بواسطة os.listdir() واحداً تلو الآخر إلى هذه الدوال للتحقق مما إذا كانت ملفات (أو دلائل، على التوالي). على سبيل المثال:
إذا كان لدينا سطر التعليمات البرمجية هذا:
filter(os.path.isfile, os.listdir())))
و os.listdir() تُرجع هذه القائمة:
[ 'Diagrams.ppt' , 'Directory 1' , 'Directory 2' , 'script.py' ]
يتم تمرير العنصر الأول من القائمة ('Diagrams.ppt') كوسيط إلى os.path.isfile() للتحقق مما إذا كان ملفاً:
os.path.isfile( 'Diagrams.ppt' ) # True

استدعاء الدالة يُرجع True، لذا فهو ملف ويتم تضمينه في القائمة.
ولكن إذا كان العنصر دليلاً:
os.path.isfile( 'Directory 1' ) # False

استدعاء الدالة يُرجع False، لذا لا يتم تضمينه في القائمة. تستمر هذه العملية لكل عنصر في القائمة حتى تحتوي القائمة الجديدة على أسماء الملفات فقط. ثم، بما أن filter() تُرجع كائناً قابلاً للتكرار (iterable)، فإننا ننشئ قائمة من هذا الكائن القابل للتكرار باستخدام list():
list(filter(os.path.isfile, os.listdir()))
ونقوم بطباعتها لأننا نعمل بملف بايثون (برنامج نصي):
print(list(filter(os.path.isfile, os.listdir())))
نصيحة: يمكنك التعرف بصرياً على ما إذا كان عنصر من القائمة يمثل ملفاً أو دليلاً من خلال رؤية ما إذا كان لديه امتداد (نوع) بعد اسمه. على سبيل المثال: Diagrams.ppt لديه امتداد .ppt يخبرك أنه ملف PowerPoint، لكن الدليل ليس لديه امتداد، مثل 'Directory 1'.

دالة system("ls"): تنفيذ أوامر نظام التشغيل
الآن بعد أن عرفت كيفية العمل مع listdir()، دعنا نرى كيف تعمل دالة system() خلف الكواليس وكيف يمكنك استخدامها.
الغرض
وفقاً لوثائق بايثون، الغرض من دالة system() هو:
تنفيذ الأمر (سلسلة نصية) في صدفة فرعية (
subshell).
بشكل أساسي، تأخذ هذه الدالة أمراً (كسلسلة نصية) وتقوم بتنفيذه. في هذه الحالة، الأمر الذي نمرره هو 'ls'، وهو أمر Unix يستخدم في Linux لعرض محتوى الدليل كإخراج قياسي (standard output).
على عكس listdir()، لن تُرجع دالة system() قائمة إذا مررنا الأمر 'ls'، بل ستعرض فقط قائمة الملفات والدلائل كإخراج قياسي. لذلك، يجب عليك استخدامها إذا كنت ترغب فقط في تصور القائمة دون العمل بها فعلياً في برنامجك.
التركيب والوسيط
لاستدعاء هذه الدالة، ستحتاج إلى استخدام هذا التركيب:

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

وإذا استدعيت دالة system()، هكذا:
>>> import os
>>> os.system( "ls" )
هذا هو الإخراج:
'Directory 1' 'file 1.py' 'file 2.py' main.py
0
يمكننا رؤية الإخراج القياسي في الطرفية (قائمة الملفات والدلائل):
'Directory 1' 'file 1.py' 'file 2.py' main.py
والقيمة المرجعة:
0
ملاحظة: لهذه الأمثلة على دالة system()، أعمل باستخدام أداة سطر أوامر عبر الإنترنت تسمى Repl.it نظراً لأن جهاز الكمبيوتر الخاص بي يعمل بنظام Windows ولا يتم التعرف على الأمر ls بواسطة موجه الأوامر الافتراضي.
القيود
أحد القيود الرئيسية لهذه الدالة هو أن الأمر الممرر كوسيط يجب أن يكون معروفاً من قبل نظام التشغيل أو البيئة التي تعمل بها. على سبيل المثال، لن يتم التعرف على الأمر ls في Windows افتراضياً في موجه الأوامر. سترى هذا الخطأ إذا حاولت تشغيله:
'ls' is not recognized as an internal or external command, operable program or batch file.
أمر مشابه في Windows سيكون الأمر 'dir':
os.system( 'dir' )
نصيحة: هناك طرق بديلة لتشغيل الأمر ls على Windows، مثل استخدام برامج طرفية تتعرف على أوامر Unix، ولكن افتراضياً لا يتعرف Windows على الأمر 'ls'.
القيمة المرجعة
وفقاً لوثائق بايثون:
على نظام Unix، القيمة المرجعة هي حالة الخروج للعملية المشفرة بالتنسيق المحدد لـ
wait().
و…
على نظام Windows، القيمة المرجعة هي تلك التي تُرجعها صدفة النظام بعد تشغيل الأمر.
نصيحة: لاحظ أن هذه الدالة لا تُرجع قائمة. إنها تعرض القائمة ببساطة كإخراج قياسي، لذا لا يمكنك تخزينها في متغير كما فعلت مع listdir().
تنوعات أمر ls
ميزة رئيسية لـ os.system('ls') هي أنها تحتوي على العديد من الخيارات المفيدة والمثيرة للاهتمام لتخصيص كيفية عرض الإخراج. دعنا نرى بعضها.
- الخيار 1: يمكننا عرض المزيد من المعلومات حول الملفات والدلائل مثل حجمها وموقعها وتاريخ ووقت تعديلها باستخدام الأمر
ls -l.
>>> import os
>>> os.system( 'ls -l' )
total 12
drwxr-xr-x 1 runner runner 0 Apr 3 18 : 23 'Directory 1'
-rw-r--r-- 1 runner runner 11 Apr 3 18 : 38 'file 1.py'
-rw-r--r-- 1 runner runner 11 Apr 3 18 : 38 'file 2.py'
-rw-r--r-- 1 runner runner 11 Apr 3 18 : 38 main.py
0
- الخيار 2: للتمكن من التعرف بصرياً على الدلائل بشكل أسرع، يمكننا استخدام
ls -F، الذي يضيف شرطة مائلة أمامية/إلى نهاية أسمائها (انظر'Directory 1/'أدناه).
>>> import os
>>> os.system( 'ls -F' )
'Directory 1' /
'file 1.py'
'file 2.py'
main.py
0
- الخيار 3: لفرز الملفات حسب الحجم، يمكننا استخدام الأمر
ls -lS.
>>> import os
>>> os.system( 'ls -lS' )
total 12
-rw-r--r-- 1 runner runner 11 Apr 3 18 : 38 'file 1.py'
-rw-r--r-- 1 runner runner 11 Apr 3 18 : 38 'file 2.py'
-rw-r--r-- 1 runner runner 11 Apr 3 18 : 38 main.py
drwxr-xr-x 1 runner runner 0 Apr 3 18 : 23 'Directory 1'
0
هناك العديد من الخيارات الأخرى للتخصيص التي يمكن أن تكون مفيدة لهدفك الخاص. هنا يمكنك العثور على مزيد من المعلومات حول أمر ls وكيف يمكنك استخدام قوته الكاملة.
ملخص مقارن: listdir() مقابل system("ls")
لتلخيص الفروقات الرئيسية بين الدالتين، إليك مقارنة سريعة:
- الغرض:
listdir(): تُرجع قائمة بأسماء الملفات والدلائل في المسار المحدد (افتراضياً، الدليل التشغيلي الحالي)، مما يتيح لك معالجتها برمجياً.system("ls"): تعرض فقط الملفات والدلائل كإخراج قياسي إلى الطرفية، ولا تُرجع قائمة يمكن التفاعل معها داخل البرنامج.
- نظام التشغيل:
listdir(): يمكن استخدامها بشكل مستقل عن نظام التشغيل الذي تعمل به، فهي تعمل حيثما يعمل بايثون.system('ls'): يجب تنفيذها في نظام تشغيل أو بيئة تتعرف على الأمر'ls'(مثل أنظمة Unix/Linux). في Windows، ستحتاج إلى بديل مثل'dir'أو بيئة طرفية تدعم أوامر Unix.
- التخصيص:
listdir(): يمكنك تصفية القائمة المرجعة إذا كنت بحاجة إلى إزالة الملفات أو الدلائل باستخدام دالةfilter()، مما يوفر مرونة برمجية عالية.system('ls'): يمكنك تمرير خيارات (flags) لتخصيص إخراج الأمر'ls'(مثل-lأو-Fأو-lS)، مما يوفر تحكماً في طريقة العرض.
الخلاصة التقنية
في عالم بايثون، تُعد إدارة نظام الملفات جانباً حيوياً لأي مطور. لقد استعرضنا في هذا المقال أداتين أساسيتين من وحدة os: دالة listdir() ودالة system("ls"). يكمن الاختلاف الجوهري بينهما في طبيعة التفاعل مع نظام التشغيل وفي القيمة المرجعة. بينما توفر listdir() قائمة برمجية قابلة للمعالجة، مما يجعلها مثالية للمهام التي تتطلب معالجة منطقية للملفات والدلائل (مثل الفرز، التصفية، أو تطبيق عمليات على كل عنصر)، فإن system("ls") هي بمثابة "نافذة عرض" سريعة لمحتويات الدليل، مفيدة للتحقق البصري السريع أو لتشغيل أوامر نظام التشغيل مباشرة. اختيار الدالة المناسبة يعتمد كلياً على متطلبات مهمتك: هل تحتاج إلى بيانات قابلة للمعالجة برمجياً، أم مجرد إخراج نصي سريع؟ فهم هذه الفروق الدقيقة يمنح المطور القدرة على كتابة أكواد بايثون أكثر كفاءة وقوة في التعامل مع نظام الملفات.