عنوان:

‫لینوکس برای توسعه‌دهندگان دات‌نت: از ویندوز تا کراس-پلتفرم؛ بخش اول: تفاوت‌های بنیادین سیستم‌عامل


نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۴/۲۵ ۰۸:۰۵
آدرس: www.dntips.ir
به بخش اول این سری خوش آمدید! در این بخش، به سراغ اساسی‌ترین تفاوت‌های بین ویندوز و لینوکس می‌رویم؛ تفاوت‌هایی که شاید در ابتدا ساده به نظر برسند، اما ندانستن آن‌ها منشأ بسیاری از باگ‌های غیرمنتظره برای توسعه‌دهندگان دات‌نت است. هدف ما این است که با درک این تفاوت‌ها، کدهایی بنویسیم که از روز اول روی هر دو پلتفرم به درستی کار کنند.

۱. ساختار فایل سیستم (File System Structure)

بزرگ‌ترین تفاوت ظاهری بین ویندوز و لینوکس، نحوه‌ی آدرس‌دهی فایل‌ها و پوشه‌هاست.
  • در ویندوز: همه چیز با درایوها شروع می‌شود (مانند C:\، D:\ و...). هر درایو یک ریشه‌ی جداگانه دارد و ساختار فایل‌ها زیرمجموعه‌ی آن است.
  • مثال: C:\Users\Me\Documents\MyProject
  • در لینوکس: همه چیز زیر یک ریشه‌ی واحد به نام / قرار دارد. در لینوکس مفهومی به نام "درایو" وجود ندارد. سایر پارتیشن‌ها و دستگاه‌ها (مانند فلش مموری) در پوشه‌هایی زیر این ریشه‌ی اصلی mount (سوار) می‌شوند.
  • مثال: /home/me/documents/myproject
این تفاوت به این معنی است که شما هرگز نباید یک مسیر را به صورت ثابت و با حرف درایو ("C:\\logs\\log.txt") در کد خود بنویسید، زیرا این مسیر در لینوکس کاملاً بی‌معنی است.

۲. حساسیت به بزرگی و کوچکی حروف (Case Sensitivity) ⚠️

این مورد بدون شک مهم‌ترین تله برای توسعه‌دهندگانی است که از ویندوز به لینوکس مهاجرت می‌کنند.
  • در ویندوز (Case-Insensitive): سیستم‌عامل تفاوتی بین حروف بزرگ و کوچک در نام فایل‌ها و پوشه‌ها قائل نیست. فایل‌های MyFile.txt، myfile.txt و MYFILE.TXT همگی به یک فایل واحد اشاره دارند.
  • در لینوکس (Case-Sensitive): بزرگی و کوچکی حروف مهم است. فایل‌های MyFile.txt، myfile.txt و MYFILE.TXTسه فایل کاملاً مجزا هستند.

مثال فاجعه‌بار:
فرض کنید کدی برای خواندن یک فایل کانفیگ نوشته‌اید:
// این کد در ویندوز بدون مشکل کار می‌کند،
// حتی اگر نام واقعی فایل "appsettings.json" باشد.
string configJson = File.ReadAllText("AppSettings.json");
این کد روی سرور لینوکس، اگر نام فایل دقیقاً AppSettings.json نباشد و مثلاً appsettings.json باشد، بلافاصله با خطای FileNotFoundException مواجه خواهد شد.

نکته کلیدی: همیشه نام فایل‌ها را با همان بزرگ و کوچکی حروفی که در سیستم فایل وجود دارند، در کد خود استفاده کنید. بهترین راهکار این است که یک استاندارد برای نام‌گذاری فایل‌ها در پروژه‌ی خود تعریف کنید (مثلاً استفاده از حروف کوچک) و همیشه به آن پایبند باشید.

۳. کاراکتر جداساز مسیر (Path Separator)

یکی دیگر از تفاوت‌های کلیدی که مستقیماً روی کدهای مرتبط با فایل سیستم تأثیر می‌گذارد.
  • در ویندوز: از بک‌اسلش \ برای جدا کردن پوشه‌ها استفاده می‌شود. مثال: C:\Windows\System32
  • در لینوکس: از اسلش / برای جدا کردن پوشه‌ها استفاده می‌شود. مثال: /var/log
اشتباه رایج: چسباندن رشته‌ها برای ساختن مسیر فایل.
// 👎 کد وابسته به پلتفرم (فقط در ویندوز کار می‌کند)
string userName = "myuser";
string filePath = "C:\\Users\\" + userName + "\\Documents\\report.pdf";

✅ راه‌حل طلایی: استفاده از Path.Combine
کلاس System.IO.Path در دات‌نت دوست شماست. متد Combine به صورت هوشمند و بر اساس سیستم‌عاملی که کد روی آن اجرا می‌شود، از کاراکتر جداساز صحیح استفاده می‌کند.
// 👍 کد کراس-پلتفرم و صحیح
string userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); // مسیر پوشه home کاربر را به درستی در هر دو سیستم‌عامل می‌دهد
string fileName = "report.pdf";

// Path.Combine به طور خودکار از "/" در لینوکس و "\" در ویندوز استفاده می‌کند
string fullPath = Path.Combine(userProfile, "Documents", fileName); 

Console.WriteLine(fullPath);
خروجی در ویندوز: C:\Users\myuser\Documents\report.pdf
خروجی در لینوکس: /home/myuser/Documents/report.pdf

همچنین برای دسترسی مستقیم به کاراکتر جداساز، می‌توانید از Path.DirectorySeparatorChar استفاده کنید، هرچند Path.Combine معمولاً گزینه‌ی بهتری است.

۴. مدیریت مجوزها (Permissions)

در ویندوز، معمولاً برنامه‌ها با سطح دسترسی کاربر اجرا می‌شوند و مشکلات مجوز کمتر به چشم می‌آیند. در لینوکس، مدل مجوزها بسیار سخت‌گیرانه‌تر است.
مدل مجوز در لینوکس: هر فایل و پوشه دارای سه سطح مجوز است:
  • خواندن (Read - r)
  • نوشتن (Write - w)
  • اجرا (Execute - x)
این مجوزها برای سه دسته تعریف می‌شوند:
  • مالک (User): کسی که فایل را ایجاد کرده.
  • گروه (Group): گروهی که مالک فایل به آن تعلق دارد.
  • دیگران (Others): سایر کاربران سیستم.

مشکل رایج: اپلیکیشن شما تلاش می‌کند یک فایل لاگ در پوشه‌ی /var/log/myapp/ ایجاد کند، اما با خطای System.UnauthorizedAccessException: Access to the path '/var/log/myapp/app.log' is denied مواجه می‌شود.
دلیل: کاربری که برنامه را اجرا کرده (مثلاً www-data برای یک وب سرور)، مجوز نوشتن (Write) در آن پوشه را ندارد.
راه‌حل‌های سریع:
  • بررسی و تغییر مجوزها: با استفاده از دستور chmod می‌توانید مجوزها را تغییر دهید. برای مثال، برای دادن مجوز اجرا به فایل اپلیکیشن خود:
chmod +x ./MyWebApp
  • تغییر مالکیت: با دستور chown می‌توانید مالک فایل یا پوشه را به کاربری که برنامه را اجرا می‌کند تغییر دهید.
# مالکیت پوشه لاگ را به کاربر www-data می‌دهیم
chown -R www-data:www-data /var/log/myapp

۵. پایان خطوط (Line Endings)

این تفاوت کوچک می‌تواند باعث مشکلات بزرگی در خواندن فایل‌های متنی، اسکریپت‌ها یا حتی در سیستم کنترل ورژن (Git) شود.
  • در ویندوز: پایان هر خط با دو کاراکتر مشخص می‌شود: Carriage Return (CR) و Line Feed (LF) که به صورت \r\n نمایش داده می‌شود.
  • در لینوکس: پایان هر خط فقط با یک کاراکتر Line Feed (LF) یا \n مشخص می‌شود.
مشکلات احتمالی:
  • یک شل اسکریپت (.sh) که در ویندوز نوشته شده، ممکن است در لینوکس به دلیل وجود کاراکتر \r اجرا نشود.
  • Git به شما در مورد تفاوت Line Endings در فایل‌ها هشدار می‌دهد.
  • خواندن یک فایل متنی خط به خط در #C ممکن است رفتار متفاوتی داشته باشد.
✅ راه‌حل در کد #C:
هنگام نوشتن در فایل‌های متنی، به جای استفاده از "\r\n" یا "\n" به صورت ثابت، از Environment.NewLine استفاده کنید. این پراپرتی به صورت خودکار مقدار صحیح را بر اساس سیستم‌عامل جاری برمی‌گرداند.
// 👍 کد کراس-پلتفرم برای نوشتن دو خط در یک فایل
string text = "Line 1" + Environment.NewLine + "Line 2";
File.WriteAllText("myFile.txt", text);

با درک این پنج تفاوت اساسی، شما آماده‌اید تا از بروز بسیاری از خطاهای رایج جلوگیری کرده و وارد مباحث پیشرفته‌تر کدنویسی کراس-پلتفرم شوید.