- LoopX
- لینوکس از صفر
- فایلها و پوشهها
- لینکها: hard link و symbolic link
لینکها: hard link و symbolic link
توی این درس یاد میگیری فایل در لینوکس دو بخش دارد: اسم (در پوشه) و inode (خود فایل: محتوا و اطلاعاتش). با این دید میفهمی hard link (ln) یعنی یک inode با چند اسم و symbolic link (ln -s، «symlink») یعنی یک فایل کوچک که فقط آدرس یک فایل دیگر را دارد. با ls -i و stat inode را میبینی، با readlink -f مقصد نهایی یک لینک را پیدا میکنی، میفهمی لینک شکسته چرا پیش میآید و کاربردهای واقعی (مثل sites-enabled در nginx و نسخهی current) را میبینی.
مسئله: یک فایل، چند جا
Section titled “مسئله: یک فایل، چند جا”گاهی یک فایل باید از چند جا در دسترس باشد: تنظیمات nginx که باید هم در sites-available و هم sites-enabled دیده شود؛ نسخهی فعلی یک برنامه که current نامیده میشود ولی هر بار به نسخهی دیگری اشاره میکند؛ یا یک فایل بزرگ که نمیخواهی کپیاش کنی. کپی کردن حجم و هماهنگی میخواهد. راهحل: لینک.
تشبیه: شمارهی پرونده و برچسب روی قفسه
Section titled “تشبیه: شمارهی پرونده و برچسب روی قفسه”inode مثل شمارهی پرونده در بایگانی است: محتوا و مشخصات (حجم، مالک، دسترسیها، زمانها) زیر آن شماره است، و خود شماره اسم ندارد. اسم فایل فقط یک برچسب روی قفسه است که میگوید «پروندهی شمارهی ۱۲۳ اینجاست». یک hard link یعنی برچسب دوم برای همان پرونده. یک symlink یعنی برگهای که روی آن نوشته شده «برو ببین قفسهی …» (آدرس یک برچسب دیگر).
مثالهای عملی
Section titled “مثالهای عملی”مثال ۱: inode را ببین
Section titled “مثال ۱: inode را ببین”هر فایل یک شمارهی inode دارد. ls -i آن را کنار اسم چاپ میکند و stat جزئیات بیشتر:
mkdir -p ex1 && cd ex1echo "محتوای اصلی" > original.txtls -i original.txtecho "--- stat:"stat -c 'inode=%i تعداد لینک=%h حجم=%s نام=%n' original.txt112341 original.txt--- stat:inode=112341 تعداد لینک=1 حجم=22 نام=original.txtعدد اول inode است و تعداد لینک (%h) برابر ۱: فقط یک اسم به این inode اشاره میکند. (دستور stat را بدون flag بزن تا همهی اطلاعات را ببینی.)
مثال ۲: hard link، دو اسم، یک فایل
Section titled “مثال ۲: hard link، دو اسم، یک فایل”mkdir -p ex2 && cd ex2echo "محتوای اصلی" > original.txtln original.txt hard.txtls -li original.txt hard.txtecho "--- تعداد لینک:"stat -c '%n: inode=%i links=%h' original.txt hard.txt113275 -rw-r--r-- 2 ali ali 22 Oct 3 20:04 hard.txt113275 -rw-r--r-- 2 ali ali 22 Oct 3 20:04 original.txt--- تعداد لینک:original.txt: inode=113275 links=2hard.txt: inode=113275 links=2هر دو همان inode را دارند و تعداد لینک ۲ شد. دو اسماند برای یک فایل، نه کپی. اثبات: تغییر از یکی، در دیگری دیده میشود:
cd ex2echo "خط اضافه از طریق hard.txt" >> hard.txtecho "--- original.txt:"cat original.txtecho "--- حذف original.txt و خواندن hard.txt:"rm original.txtcat hard.txtstat -c '%n: links=%h' hard.txt--- original.txt:محتوای اصلیخط اضافه از طریق hard.txt--- حذف original.txt و خواندن hard.txt:محتوای اصلیخط اضافه از طریق hard.txthard.txt: links=1با حذف اسم original.txt، داده نرفت: hard.txt هنوز هست و تعداد لینک شد ۱. داده فقط وقتی پاک میشود که تعداد لینک صفر شود و هیچ برنامهای هم آن را باز نگه نداشته باشد. در واقع rm یعنی «اسم را بردار»، نه «داده را پاک کن».
مثال ۳: محدودیتهای hard link
Section titled “مثال ۳: محدودیتهای hard link”hard link فقط در یک فایلسیستم و فقط برای فایلها (نه پوشهها) ممکن است:
mkdir -p ex3 && cd ex3rm -f /tmp/a-hard.txtecho hi > a.txtln a.txt ./a-copy.txt && echo "داخل همان پوشه/دیسک: موفق"ln /etc/hostname h.txtmkdir dln d d-linkداخل همان پوشه/دیسک: موفقln: failed to create hard link 'h.txt' => '/etc/hostname': Invalid cross-device linkln: d: hard link not allowed for directoryدومی (/etc/hostname روی یک mount دیگر است) خطای Invalid cross-device link میدهد و سومی hard link not allowed for directory. این دو محدودیت یکی از دلایل وجود symlink است.
بعد از ln a.txt b.txt و rm a.txt، چه میشود؟
هر دو اسم به یک inode اشاره میکنند؛ داده تا وقتی یک لینک مانده، میماند.
مثال ۴: symlink، یک فایل کوچک با آدرس
Section titled “مثال ۴: symlink، یک فایل کوچک با آدرس”ln -s مقصد ناملینک (-s یعنی symbolic). ترتیب مثل cp است: اول «به چه چیزی»، بعد «اسم جدید»:
mkdir -p ex4 && cd ex4echo "تنظیمات برنامه" > config.yamlln -s config.yaml settings.yamlls -l config.yaml settings.yamlecho "--- inode ها:"ls -i config.yaml settings.yamlecho "--- محتوا از طریق لینک:"cat settings.yaml-rw-r--r-- 1 ali ali 28 Oct 3 20:04 config.yamllrwxrwxrwx 1 ali ali 11 Oct 3 20:04 settings.yaml -> config.yaml--- inode ها:299914 config.yaml299916 settings.yaml--- محتوا از طریق لینک:تنظیمات برنامهدر ls -l اولین حرف l است (link) و بعد از اسم -> config.yaml مقصد را نشان میدهد. حجم لینک ۱۱ بایت است (طول متن «config.yaml»؛ یک symlink فقط آدرس را نگه میدارد) و inodeها متفاوتاند: symlink یک فایل جداست. دستورها (cat، nano) لینک را دنبال میکنند و به مقصد میرسند.
با readlink فقط مقصد را میپرسی و readlink -f مسیر نهایی و کامل را (همهی لینکها را باز میکند):
cd ex4ln -s settings.yaml alias.yamlreadlink alias.yamlreadlink -f alias.yamlsettings.yaml/home/ali/ex4/config.yamlیک زنجیرهی دو مرحلهای: alias.yaml → settings.yaml → config.yaml؛ -f تا آخر میرود.
مثال ۵: لینک شکسته
Section titled “مثال ۵: لینک شکسته”symlink فقط یک آدرس است؛ اگر مقصد جابهجا یا حذف شود، لینک میماند ولی به جایی اشاره میکند که نیست:
mkdir -p ex5 && cd ex5echo "داده" > target.txtln -s target.txt mylinkcat mylinkmv target.txt moved.txtecho "--- بعد از جابهجا کردن مقصد:"ls -l mylinkcat mylinktest -e mylink && echo "لینک سالم" || echo "لینک شکسته است"داده--- بعد از جابهجا کردن مقصد:lrwxrwxrwx 1 ali ali 10 Oct 3 20:04 mylink -> target.txtcat: mylink: No such file or directoryلینک شکسته استls -l هنوز mylink -> target.txt را نشان میدهد، ولی cat میگوید فایل نیست؛ در بسیاری از ترمینالها اسم لینک شکسته قرمز میشود. test -e (آیا وجود دارد؟) مقصد را دنبال میکند و شکسته را تشخیص میدهد. راهحل: مقصد را برگردان یا لینک را دوباره با آدرس درست بساز. این همان تمرین درس است.
مثال ۶: مسیر نسبی در برابر مطلق
Section titled “مثال ۶: مسیر نسبی در برابر مطلق”مقصد symlink میتواند مطلق (/home/ali/data) یا نسبی باشد. مهم: مقصد نسبی نسبت به پوشهی خودِ لینک حساب میشود، نه جایی که تو هستی:
mkdir -p ex6 && cd ex6mkdir -p proj/conf proj/binecho "x=1" > proj/conf/app.confcd proj/binln -s ../conf/app.conf app.confls -l app.confcat app.confecho "--- جابهجا کردن لینک به یک پوشهی دیگر:"mv app.conf ../cat ../app.conflrwxrwxrwx 1 ali ali 16 Oct 3 20:04 app.conf -> ../conf/app.confx=1--- جابهجا کردن لینک به یک پوشهی دیگر:cat: ../app.conf: No such file or directoryلینک bin/app.conf با آدرس نسبی ../conf/app.conf کار میکرد، ولی وقتی لینک را یک پوشه بالاتر بردیم، همان آدرس از جای جدید به proj/../conf/app.conf (جایی که نیست) اشاره میکند و میشکند. قاعده: لینک نسبی با جابهجا شدن همراه با مقصدش (کل پوشه) سالم میماند؛ لینک مطلق به جای ثابتی اشاره میکند ولی با جابهجا شدن پروژه میشکند.
مثال ۷: symlink به پوشه، pwd -P
Section titled “مثال ۷: symlink به پوشه، pwd -P”mkdir -p ex7/releases/v1 && cd ex7echo "نسخه ۱" > releases/v1/info.txtln -s releases/v1 currentcd currentecho "pwd (منطقی): $(pwd)"echo "pwd -P (واقعی): $(pwd -P)"cat info.txtpwd (منطقی): /home/ali/ex7/currentpwd -P (واقعی): /home/ali/ex7/releases/v1نسخه ۱وقتی از طریق symlink وارد پوشه میشوی، shell مسیر منطقی (با اسم لینک) را نگه میدارد و pwd -P (physical) مسیر واقعی را میدهد. و cd -P current بهجای ماندن در لینک، مسیر واقعی را وارد میکند.
مثال ۸: کاربرد واقعی، جابهجایی نسخه (ln -sfn)
Section titled “مثال ۸: کاربرد واقعی، جابهجایی نسخه (ln -sfn)”همان الگوی سناریو: current را از v1 به v2 ببر، بدون تغییر سرویس:
mkdir -p ex8/releases/v1 ex8/releases/v2 && cd ex8echo "نسخه ۱" > releases/v1/info.txtecho "نسخه ۲" > releases/v2/info.txtln -s releases/v1 currentecho "الان: $(cat current/info.txt) ← $(readlink current)"ln -sfn releases/v2 currentecho "بعد از انتشار: $(cat current/info.txt) ← $(readlink current)"ln -sfn releases/v1 currentecho "rollback: $(cat current/info.txt) ← $(readlink current)"الان: نسخه ۱ ← releases/v1بعد از انتشار: نسخه ۲ ← releases/v2rollback: نسخه ۱ ← releases/v1-s symlink، -f (force: اگر لینک از قبل هست جایگزین کن) و -n (no-dereference: لینک موجود به یک پوشه را مثل فایل ببین، نه اینکه داخل مقصدش لینک بسازد). بدون -n، ln -sf releases/v2 current وقتی current به یک پوشه اشاره میکند، لینک جدیدی داخل releases/v1 میسازد، اشتباه رایج.
مثال ۹: لینکهایی که همین الآن روی سیستم داری
Section titled “مثال ۹: لینکهایی که همین الآن روی سیستم داری”لینوکس پر از symlink است:
ls -l /bin /etc/os-release /usr/bin/python3 /usr/bin/editor 2>&1 | cut -c1-110echo "--- sites-enabled در nginx:"ls -l /etc/nginx/sites-enabled/echo "--- مقصد نهایی python3:"readlink -f /usr/bin/python3lrwxrwxrwx 1 root root 7 Apr 22 2024 /bin -> usr/binlrwxrwxrwx 1 root root 21 Sep 6 14:29 /etc/os-release -> ../usr/lib/os-releaselrwxrwxrwx 1 root root 24 Jun 3 22:50 /usr/bin/editor -> /etc/alternatives/editorlrwxrwxrwx 1 root root 10 Nov 12 2025 /usr/bin/python3 -> python3.12--- sites-enabled در nginx:total 0lrwxrwxrwx 1 root root 34 Oct 3 09:34 default -> /etc/nginx/sites-available/default--- مقصد نهایی python3:/usr/bin/python3.12/bin به usr/bin اشاره میکند (درس ساختار پوشهها)؛ python3 به نسخهی مشخص (python3.12)، تا بتوانی نسخه را عوض کنی؛ و در nginx تنظیم هر سایت در sites-available نوشته میشود و برای فعال کردن فقط یک symlink در sites-enabled میسازی (و برای غیرفعال کردن، همان لینک را پاک میکنی؛ فایل اصلی میماند).
مثال ۱۰: حذف نام و «فایل پاکشدهی هنوز باز»
Section titled “مثال ۱۰: حذف نام و «فایل پاکشدهی هنوز باز»”داده تا وقتی باز باشد نمیمیرد، حتی بعد از rm. ببین:
mkdir -p ex10 && cd ex10echo "این داده بعد از rm هم خوانده میشود" > ghost.txtexec 3< ghost.txt # فایل را با شمارهی 3 باز نگه دارrm ghost.txtls ghost.txt 2>&1echo "--- ولی هنوز از طریق fd 3 قابلخواندن است:"cat <&3exec 3<&- # بستنls: cannot access 'ghost.txt': No such file or directory--- ولی هنوز از طریق fd 3 قابلخواندن است:این داده بعد از rm هم خوانده میشوداسم رفت (ls خطا داد) ولی چون یک پروسه (shell ما) فایل را باز نگه داشته بود، inode و محتوا ماندند. همین است که گاهی df میگوید دیسک پر است ولی du چیزی پیدا نمیکند: فایلی حذف شده ولی هنوز یک پروسه آن را باز دارد (راهحل: پروسه را ریاستارت کن؛ درس منابع سیستم).
پشت پرده: inode و جدول پوشه
Section titled “پشت پرده: inode و جدول پوشه”فایلسیستمهای لینوکسی (ext4 و…) هر فایل را با یک inode نگه میدارند که شامل نوع، دسترسیها، مالک، حجم، زمانها و اشارهگر به بلوکهای داده است؛ اسم فایل در inode نیست. پوشه یک فایل ویژه است که فقط جدولی از «اسم ← شمارهی inode» دارد. پس:
- hard link = یک ردیف دیگر در جدول یک پوشه (شاید همان پوشه یا پوشهی دیگر در همان فایلسیستم) که به همان شمارهی inode اشاره میکند. هسته یک شمارندهی لینک در inode نگه میدارد و فقط وقتی به صفر برسد (و باز نباشد)، بلوکها آزاد میشوند. چون شمارهی inode فقط داخل یک فایلسیستم یکتاست، hard link بین فایلسیستمها ممکن نیست؛ و برای پوشهها ممنوع است تا حلقهی بیپایان (پوشهای که خودش را شامل شود) ساخته نشود.
- symlink = یک inode با نوع «لینک» که متنی (مسیر مقصد) در خود دارد. هسته هنگام باز کردن مسیر، در صورت دیدن symlink، متن را میخواند و مسیر را از نو ادامه میدهد؛ برای همین میتواند به هر جا (حتی فایلسیستم دیگر یا مقصدی که هنوز وجود ندارد) اشاره کند.
حلقهی symlink هم ممکن است (a → b → a)؛ هسته بعد از چند مرحله (حدود ۴۰) خطای Too many levels of symbolic links میدهد.
جدولهای مرجع
Section titled “جدولهای مرجع”| دستور | کار |
|---|---|
ln مقصد نام |
hard link |
ln -s مقصد نام |
symbolic link |
ln -sfn مقصد نام |
symlink را (با جایگزینی لینک موجود) بساز |
ls -i |
نمایش inode |
ls -l |
نمایش لینکها با l و -> |
stat فایل |
همهی جزئیات inode و تعداد لینک |
readlink لینک |
مقصد مستقیم |
readlink -f لینک |
مقصد نهایی و کامل |
test -e لینک |
آیا مقصد وجود دارد؟ |
pwd -P / cd -P |
مسیر واقعی (بدون لینک) |
| hard link | symlink | |
|---|---|---|
| inode | همان inode | inode خودش |
| حذف مقصد | داده میماند | لینک میشکند |
| پوشه | ممکن نیست | ممکن |
| بین دیسکها | ممکن نیست | ممکن |
تشخیص در ls -l |
عدد لینک > ۱ | حرف l و -> |
| مصرف | هیچ (فقط یک اسم) | چند بایت |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) ترتیب اشتباه در ln -s
Section titled “۱) ترتیب اشتباه در ln -s”mkdir -p m1 && cd m1ln -s link-name actual-target 2>&1ls -l link-name actual-target 2>&1 | head -3ls: cannot access 'link-name': No such file or directorylrwxrwxrwx 1 ali ali 9 Oct 3 20:04 actual-target -> link-nameترتیب: ln -s **مقصد** **ناملینک**. اگر اشتباه بنویسی یک لینک شکسته میسازی. راهحل: به خاطر بسپار «مثل cp: از کجا ← به کجا».
۲) لینک نسبی که به جای اشتباه نسبت داده میشود
Section titled “۲) لینک نسبی که به جای اشتباه نسبت داده میشود”مثال ۶. راهحل: نسبت به پوشهی خودِ لینک فکر کن، یا از مسیر مطلق استفاده کن.
۳) ln -sf بدون -n روی لینک پوشه
Section titled “۳) ln -sf بدون -n روی لینک پوشه”mkdir -p m3 && cd m3mkdir v1 v2ln -s v1 curln -sf v2 curls -l cur v1 | head -6lrwxrwxrwx 1 ali ali 2 Oct 3 20:04 cur -> v1
v1:total 0lrwxrwxrwx 1 ali ali 2 Oct 3 20:04 v2 -> v2cur به پوشهی v1 اشاره میکند و ln -sf v2 cur داخل v1 یک لینک به اسم v2 ساخت (در خروجی v1/ حالا یک v2 دارد) و cur عوض نشد. راهحل: ln -sfn.
۴) تصور اینکه symlink بکاپ است
Section titled “۴) تصور اینکه symlink بکاپ است”اگر فایل اصلی حذف شود، symlink هیچ دادهای ندارد. راهحل: برای نسخهی پشتیبان کپی بگیر (cp -a)، نه لینک.
۵) حذف پوشهی لینک با rm -r و / در آخر
Section titled “۵) حذف پوشهی لینک با rm -r و / در آخر”برای حذف خودِ لینک از rm لینک (بدون -r و بدون / آخر) استفاده کن. با / آخر شل به مقصد میرود و ممکن است خطا بگیری یا چیز دیگری را هدف بگیری:
mkdir -p m5 && cd m5mkdir realdir && touch realdir/fileln -s realdir dirlinkrm dirlinkls realdirfilerm dirlink فقط لینک را برداشت و realdir/file سالم ماند.
یک فایل notes.txt بساز و یک hard link و یک symlink به آن بساز. با ls -li ببین کدام inode مشترک دارد.
دیدن جواب
mkdir -p t1 && cd t1echo "یادداشت" > notes.txtln notes.txt notes-hard.txtln -s notes.txt notes-sym.txtls -li notes.txt notes-hard.txt notes-sym.txt342201 -rw-r--r-- 2 ali ali 15 Oct 3 20:04 notes-hard.txt342241 lrwxrwxrwx 1 ali ali 9 Oct 3 20:04 notes-sym.txt -> notes.txt342201 -rw-r--r-- 2 ali ali 15 Oct 3 20:04 notes.txtnotes-hard.txt همان inode فایل اصلی را دارد؛ notes-sym.txt inode جدا.
تمرین اصلی: یک symlink به یک فایل تنظیمات بساز و فایل اصلی را جابهجا کن تا لینک بشکند. بعد ثابت کن شکسته است (test -e)، و با ساختن دوبارهی لینک به مسیر جدید درستش کن.
دیدن جواب
mkdir -p etc-demo && cd etc-demoecho "port=80" > app.confln -s app.conf active.confcat active.confmv app.conf app.conf.oldtest -e active.conf && echo "سالم" || echo "شکسته است"ls -l active.confln -sf app.conf.old active.confcat active.conftest -e active.conf && echo "دوباره سالم"port=80شکسته استlrwxrwxrwx 1 ali ali 8 Oct 3 20:04 active.conf -> app.confport=80دوباره سالمیک سناریوی انتشار بنویس: سه نسخهی releases/v1، v2، v3 بساز که هر کدام یک فایل VERSION دارند، یک symlink current، و یک تابع deploy که نسخه را بگیرد و با ln -sfn به آن سوییچ کند و نسخهی فعال را چاپ کند. سه بار صدایش بزن (v2، v3، v1).
دیدن جواب
mkdir -p t3 && cd t3for v in v1 v2 v3; do mkdir -p releases/$v; echo $v > releases/$v/VERSION; doneln -s releases/v1 currentdeploy() { ln -sfn "releases/$1" current && echo "فعال: $(cat current/VERSION) (لینک → $(readlink current))"; }deploy v2deploy v3deploy v1فعال: v2 (لینک → releases/v2)فعال: v3 (لینک → releases/v3)فعال: v1 (لینک → releases/v1)آزمونک
Section titled “آزمونک”hard link چیست؟
با حذف یکی، دیگری و داده میمانند.
اگر مقصد یک symlink حذف شود؟
symlink فقط آدرس دارد.
ترتیب درست ساخت symlink؟
مثل cp: اول مقصد (به چه چیزی)، بعد نام جدید.
کدام یک روی پوشهها و بین فایلسیستمها کار میکند؟
hard link محدود به یک فایلسیستم و فقط فایلهاست.
readlink -f چه میکند؟
readlink فقط یک مرحله.
برای جابهجایی نسخهی current بین v1 و v2؟
-f جایگزین میکند و -n لینک پوشه را مثل فایل میبیند.
جمعبندی
Section titled “جمعبندی”- فایل = inode (محتوا و مشخصات) + اسم (ردیفی در جدول یک پوشه).
ls -iوstatinode را نشان میدهند. - hard link (
ln a b): اسم دوم برای همان inode؛ حذف یکی، دیگری سالم؛ فقط فایل و فقط داخل یک فایلسیستم. - symlink (
ln -s مقصد نام): فایل کوچکی با آدرس مقصد؛ روی پوشه و بین دیسکها کار میکند؛ اگر مقصد برود میشکند. - لینک نسبی از پوشهی خودِ لینک حساب میشود؛
readlink -fمقصد نهایی؛ln -sfnبرای سوییچ امن لینک پوشهها (current). rmیعنی «اسم را بردار»؛ داده تا وقتی لینک یا پروسهی بازکننده باشد میماند.- نمونههای واقعی:
/bin → usr/bin،python3 → python3.12، nginxsites-enabled، نسخهیcurrent.
| دستور | کاری که میکند |
|---|---|
ln original hard | hard link |
ln -s target link | symbolic link |
ln -sfn releases/v2 current | سوییچ لینک پوشه (انتشار) |
ls -li | inode و لینکها |
stat -c "%i %h %n" f | inode و تعداد لینک |
readlink link | مقصد مستقیم |
readlink -f link | مقصد نهایی کامل |
test -e link | لینک سالم است؟ |
pwd -P | مسیر واقعی (بدون symlink) |