পাঠ ৩৮ · ৫৬-এর মধ্যে · মডিউল ৯
Home / Courses / Operating Systems (OS) / ডিরেক্টরি স্ট্রাকচার

ডিরেক্টরি স্ট্রাকচার

Directory structures
৭ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • ডিরেক্টরি কী এবং কেন এটি ফাইল সিস্টেমের সংগঠনের কেন্দ্রীয় ডেটা স্ট্রাকচার
  • চার ধরনের ডিরেক্টরি স্ট্রাকচার এবং প্রতিটি কোন সমস্যা সমাধান করে (বা করে না)
  • পাথ রেজোলিউশন ঠিক কীভাবে ধাপে ধাপে একটি ট্রি হেঁটে ফাইল খুঁজে বের করে
  • Python-এ নেস্টেড dict দিয়ে একটি বাস্তব ট্রি ডিরেক্টরি ও resolve_path() ফাংশন বাস্তবায়ন, সফল ও ব্যর্থ উভয় ক্ষেত্রেই

১ · ডিরেক্টরি কী

ডিরেক্টরি (Directory)Directoryনিজেই এক ধরনের বিশেষ ফাইল বা OS ডেটা স্ট্রাকচার, যা ফাইলনামকে তাদের মেটাডেটা/লোকেশনের সাথে ম্যাপ করে — সামগ্রিক ফাইল সিস্টেমকে সংগঠিত করে। নিজেই এক ধরনের বিশেষ ফাইল (বা OS-এর একটি ডেটা স্ট্রাকচার) — যা ফাইলনামকে তাদের L37-এ দেখা মেটাডেটা/লোকেশনের সাথে ম্যাপ করে। যদি L37-এর একটি একক ফাইলের অ্যাট্রিবিউট টেবিল কল্পনা করেন, ডিরেক্টরি হলো সেই টেবিলগুলোকে গোছানোভাবে সংগঠিত করার কাঠামো — যাতে হাজারো ফাইলের মধ্যে থেকে সঠিকটি দ্রুত খুঁজে পাওয়া যায়।

২ · চার ধরনের ডিরেক্টরি স্ট্রাকচার

সিঙ্গেল-লেভেল (ঐতিহাসিক)
সব ফাইল একটিমাত্র ফ্ল্যাট নেমস্পেসে — সহজ, কিন্তু প্রতিটি ফাইলের নাম গ্লোবালি ইউনিক হতে হয়, কয়েকটির বেশি ফাইলেই নাম-সংঘর্ষ (collision) শুরু হয়ে যায়।
টু-লেভেল
প্রতি ইউজারের নিজস্ব ডিরেক্টরি — ইউজারদের মধ্যে নাম-সংঘর্ষ সমাধান করে, কিন্তু একজন ইউজার নিজের ফাইলকে সাব-গ্রুপে ভাগ করতে পারেন না।
ট্রি-স্ট্রাকচার্ড (আজকের স্ট্যান্ডার্ড)
ডিরেক্টরির ভেতরে ফাইল ও সাব-ডিরেক্টরি উভয়ই থাকতে পারে, অসীম গভীরতার একটি ট্রি তৈরি করে — একটি সম্পূর্ণ পাথ ফাইলকে ইউনিকভাবে চিহ্নিত করে।
অ্যাসাইক্লিক-গ্রাফ
একটি ফাইল/ডিরেক্টরি একাধিক জায়গায় "শেয়ার" হতে পারে লিংকের মাধ্যমে (Unix হার্ড/সফট লিংক) — কিন্তু প্রকৃত সাইকেল (চক্র) এড়াতে সতর্কতার সাথে ডিজাইন করতে হয়, নইলে রিকার্সিভ ট্রাভার্সাল অসীম লুপে আটকে যেতে পারে।
আধুনিক প্রায় সব OS (Linux, Windows, macOS) ট্রি-স্ট্রাকচার্ড ডিরেক্টরিই ব্যবহার করে, সাথে অ্যাসাইক্লিক-গ্রাফের কিছু উপাদান (লিংক/শর্টকাট) যোগ করে। নিচের কোড সেলে আমরা মূল ট্রি-স্ট্রাকচার্ড মডেলটিই বাস্তবায়ন করব।

৩ · পাথ রেজোলিউশন — ট্রি হেঁটে ফাইল খোঁজা

একটি সম্পূর্ণ পাথ (Path)Pathরুট থেকে শুরু করে ধারাবাহিক ডিরেক্টরি নামের একটি ক্রম, যা একটি নির্দিষ্ট ফাইল/ডিরেক্টরিকে ইউনিকভাবে চিহ্নিত করে। (যেমন /home/user/documents/file.txt) রুট ডিরেক্টরি থেকে শুরু করে একের পর এক সাব-ডিরেক্টরির নাম দিয়ে ফাইলটি পর্যন্ত পৌঁছানোর একটি নির্দেশনা। পাথ রেজোলিউশন মানে এই পাথটিকে "/" দিয়ে ভেঙে প্রতিটি অংশ ধরে ধরে ট্রি-এর ভেতরে নিচের দিকে নামা — একটি অংশ না পাওয়া গেলে, বা মাঝপথে একটি ফাইলকে ডিরেক্টরি হিসেবে ব্যবহারের চেষ্টা করা হলে, রেজোলিউশন ব্যর্থ হয়ে যায়।

/ (root) home/ user/ documents/report.txt (ফাইল)
প্রতিটি ধাপে পাথের একটি অংশ মিলিয়ে ট্রি-তে একধাপ নিচে নামা হয় — সবশেষে টার্গেট ফাইলে পৌঁছায়।
Python
# L38 -- ট্রি-স্ট্রাকচার্ড ডিরেক্টরি সিমুলেশন (নেস্টেড dict, toy in-memory)
FILE_MARKER = "__FILE__"

root = {
    "home": {
        "user": {
            "documents": {
                "report.txt": FILE_MARKER,
                "notes.txt": FILE_MARKER,
            },
            "photo.png": FILE_MARKER,
        }
    }
}

def resolve_path(root_dir, path_string):
    parts = [p for p in path_string.strip("/").split("/") if p]
    current = root_dir
    walked = []
    for i, part in enumerate(parts):
        walked.append(part)
        if not isinstance(current, dict) or part not in current:
            return None, f"পাথ রিজলভ ব্যর্থ -> '/{'/'.join(walked)}' পাওয়া যায়নি"
        current = current[part]
        is_last = (i == len(parts) - 1)
        if current == FILE_MARKER and not is_last:
            return None, f"পাথ রিজলভ ব্যর্থ -> '/{'/'.join(walked)}' একটি ফাইল, ডিরেক্টরি নয় (আরও গভীরে যাওয়া যায় না)"
    return current, f"পাথ রিজলভ সফল -> '{path_string}'"


tests = [
    "/home/user/documents/report.txt",
    "/home/user/photo.png",
    "/home/user/documents",
    "/home/user/documents/missing.txt",
    "/home/nobody/file.txt",
    "/home/user/photo.png/extra",
]

for t in tests:
    result, message = resolve_path(root, t)
    print(message, "->", ("FILE" if result == FILE_MARKER else ("DIR" if isinstance(result, dict) else "N/A")))

    
লক্ষ্য করুন শেষ দুটি টেস্ট কেস — /home/nobody/file.txt ব্যর্থ হয় কারণ "nobody" নামের কোনো সাব-ডিরেক্টরিই home-এর ভেতরে নেই। আর /home/user/photo.png/extra ব্যর্থ হয় ভিন্ন কারণে — photo.png নিজেই একটি ফাইল (FILE_MARKER), তাই তার "ভেতরে" আরও কিছু খোঁজার চেষ্টা সঠিকভাবেই প্রত্যাখ্যাত হয়।
মূল কথা · Key takeaway

ডিরেক্টরি স্ট্রাকচার ঠিক করে দেয় ফাইল সিস্টেমের সামগ্রিক "নেমস্পেস" কেমন দেখতে হবে। ট্রি-স্ট্রাকচার্ড ডিরেক্টরি জিতেছে কারণ এটি সিঙ্গেল-লেভেলের নাম-সংঘর্ষ সমস্যা এবং টু-লেভেলের "সাব-গ্রুপিং করতে না পারা" সমস্যা — দুটোই একসাথে সমাধান করে, এবং পাথ রেজোলিউশনের মাধ্যমে যেকোনো গভীরতার ফাইলকে একটি একক স্ট্রিং দিয়ে ইউনিকভাবে ঠিকানা দেওয়া যায়।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ টু-লেভেল ডিরেক্টরি ইউজারদের মধ্যে নাম-সংঘর্ষ সমাধান করে, তাহলে সরাসরি ট্রি-স্ট্রাকচার্ডে না গিয়ে টু-লেভেলেই কেন থামা যেত না?

টু-লেভেল শুধু "কোন ইউজারের ফাইল" এই একস্তরের সমস্যা সমাধান করে — একজন ইউজারের নিজের শত শত ফাইল একটি ফ্ল্যাট তালিকায় থেকেই যায়, তাকে প্রজেক্ট/ফোল্ডার অনুযায়ী ভাগ করার কোনো উপায় থাকে না। ট্রি-স্ট্রাকচার্ড এই সীমাবদ্ধতা সরিয়ে দেয় — একজন ইউজার নিজেই যতগুলো ইচ্ছা সাব-ডিরেক্টরি তৈরি করে নিজের ফাইল সংগঠিত করতে পারেন, যেকোনো গভীরতায়।

প্র ০২ অ্যাসাইক্লিক-গ্রাফ ডিরেক্টরিতে "সাইকেল" (চক্র) তৈরি হলে ঠিক কোন বাস্তব সমস্যা হয়?

অনেক ফাইল-সিস্টেম অপারেশন (যেমন একটি ডিরেক্টরির মোট ডিস্ক ব্যবহার হিসাব করা, বা রিকার্সিভভাবে সব ফাইল লিস্ট করা) ধরে নেয় ট্রাভার্সাল একদিন শেষ হবে — কারণ এটি একটি ট্রি। যদি লিংকের কারণে একটি ডিরেক্টরি নিজেরই কোনো পূর্বপুরুষের (ancestor) দিকে ফিরে যায় (সাইকেল), তাহলে এই রিকার্সিভ ট্রাভার্সাল কখনও শেষ না হয়ে অসীম লুপে আটকে যেতে পারে — তাই বাস্তব সিস্টেমগুলো সাইকেল তৈরি প্রতিরোধ করে বা ট্রাভার্সালে "দেখা হয়েছে" ট্র্যাক রাখে।

প্র ০৩ কোড সেলে /home/user/photo.png/extra কেন ব্যর্থ হলো, যদিও "photo.png" পর্যন্ত পাথের প্রতিটি অংশ আসলে বিদ্যমান?

কারণ resolve_path প্রতিটি ধাপে চেক করে বর্তমান নোডটি আসলেই একটি ডিরেক্টরি (dict) কি না, যদি আরও গভীরে যাওয়ার দরকার থাকে। "photo.png"-তে পৌঁছানোর পর সেটি হলো FILE_MARKER — একটি dict নয়। যেহেতু পাথে আরও একটি অংশ ("extra") বাকি ছিল, isinstance(current, dict) চেক ব্যর্থ হয় এবং ফাংশনটি সঠিকভাবে জানায় যে একটি ফাইলকে ডিরেক্টরি হিসেবে ট্রাভার্স করার চেষ্টা করা হচ্ছে।

অনুশীলন

  1. চিন্তা করুন: আপনার নিজের কম্পিউটারে কোনো একটি ফাইলের সম্পূর্ণ পাথ লিখে ফেলুন (যেমন C:\Users\... বা /home/...) — এটি কয়টি "ধাপ"-এ ভাঙা যায়, এবং প্রতিটি ধাপ কি সত্যিই একটি ডিরেক্টরি?

    সাধারণত পাথের শেষ অংশটি বাদে (যেটি ফাইল হতে পারে) প্রতিটি অংশই একটি ডিরেক্টরি — উপরের কোড সেলের resolve_path ঠিক এই নিয়মই মেনে চলে (শেষ অংশ ছাড়া বাকি সব অংশে isinstance(current, dict) সত্য হতে হবে)। ধাপের সংখ্যা যত বেশি, পাথ তত "গভীর" — এবং প্রতিটি ধাপে একটি অতিরিক্ত ডিরেক্টরি-লুকআপ প্রয়োজন হয়, যা বাস্তব ফাইল সিস্টেমে সামান্য কিন্তু বাস্তব একটি পারফরম্যান্স খরচ।

  2. পরীক্ষা করুন: উপরের কোড সেলের root dict-এ একটি নতুন সাব-ডিরেক্টরি "downloads" যোগ করে তার ভেতরে একটি ফাইল রাখুন, তারপর সেই নতুন পাথ দিয়ে resolve_path কল করে দেখুন সঠিকভাবে খুঁজে পায় কি না।

    root["home"]["user"]["downloads"] = {"movie.mp4": FILE_MARKER} যোগ করে resolve_path(root, "/home/user/downloads/movie.mp4") কল করলে এটি সফল হবে এবং FILE_MARKER ফেরত দেবে — কারণ resolve_path কোনো হার্ডকোডেড পাথ জানে না, শুধু রানটাইমে root ট্রি-এর গঠন অনুসরণ করে। এটি দেখায় ফাংশনটি সত্যিই সাধারণ (generic) — যেকোনো নতুন ফাইল/ডিরেক্টরি যোগ করলে সাথে সাথে সেটি রেজলভযোগ্য হয়ে যায়, কোড পরিবর্তন ছাড়াই।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
ফাইল কনসেপ্ট ও ফাইল অ্যাট্রিবিউট