رفتن به محتوا
LoopX

حرکت بین پوشه‌ها

توی این درس یاد می‌گیری در درخت پوشه‌های لینوکس مثل یک نقشه‌خوان باتجربه حرکت کنی. با pwd می‌فهمی کجایی، با cd جابه‌جا می‌شوی، تفاوت مسیر مطلق (از / شروع می‌شود) و مسیر نسبی (از جایی که هستی) را می‌شناسی، علامت‌های ~ (خانه)، . (همین‌جا) و .. (یک پوشه بالاتر) را به کار می‌بری، فایل‌های مخفی را می‌بینی، خروجی ls -la و ls -lh را ستون‌به‌ستون می‌خوانی و با cd - بین دو پوشه می‌پری.

مسئله: «من الآن کجا هستم؟»

Section titled “مسئله: «من الآن کجا هستم؟»”

در محیط گرافیکی همیشه پنجره‌ی پوشه را جلوی چشمت داری. در ترمینال هیچ‌کدام نیست: فقط یک خط فرمان و یک «جای فعلی» نامرئی. اگر ندانی کجایی، دستور rm یا cp می‌تواند روی جای اشتباه اجرا شود. پس اولین مهارت لینوکس این است: همیشه بدانی کجایی و بتوانی به هر جایی بروی.

تشبیه: نشانی دادن در شهر

Section titled “تشبیه: نشانی دادن در شهر”

نشانی مطلق مثل آدرس کامل پستی است: «تهران، خیابان آزادی، پلاک ۱۰». از هر کجا بخوانی همان‌جا می‌رسی. نشانی نسبی مثل «دو تا کوچه پایین‌تر، دست راست» است: فقط از جای فعلی‌ات معنی دارد. اولی همیشه کار می‌کند ولی طولانی است؛ دومی کوتاه است ولی به جای فعلی بستگی دارد.

درختی که در این درس با آن کار می‌کنیم. مسیر مطلق همیشه از / شروع می‌شود؛ مسیر نسبی از پوشه‌ای که در آن هستی.

برای تمرین حرکت، یک درخت کوچک می‌سازیم. (دستور mkdir -p که پوشه‌ها را یکجا می‌سازد در درس بعد کامل می‌آید؛ فعلاً فقط اجرایش کن.)

Terminal window
mkdir -p projects/web/css projects/web/js projects/api/v1 docs
echo "ساخته شد:"
ls
خروجی
ساخته شد:
docs
projects

pwd (print working directory) پوشه‌ی کاری فعلی را چاپ می‌کند: همان «جای فعلی».

Terminal window
pwd
echo "پوشه‌ی خانه‌ی من: $HOME"
خروجی
/home/ali
پوشه‌ی خانه‌ی من: /home/ali

هر پروسه‌ی shell در هر لحظه فقط در یک پوشه است. pwd همیشه مسیر مطلق (کامل) را می‌دهد. ترمینال‌ها معمولاً بیشتر مواقع (در پرامپت) هم همین را نشان می‌دهند (ali@pc:~/projects$).

cd (change directory) پوشه‌ی کاری را عوض می‌کند. بدون خروجی است؛ اگر موفق شود چیزی نمی‌گوید:

Terminal window
cd /var/log
pwd
cd /
pwd
cd
pwd
خروجی
/var/log
/
/home/ali

سه نکته: ۱) cd /var/log با مسیر مطلق رفت. ۲) cd / به ریشه رفت. ۳) cd بدون argument به پوشه‌ی خانه برمی‌گردد (کوتاه‌ترین راه خانه رفتن).

مثال ۳: مسیر مطلق و نسبی

Section titled “مثال ۳: مسیر مطلق و نسبی”

اگر مسیر با / شروع شود مطلق است؛ در غیر این صورت نسبی (نسبت به پوشه‌ی فعلی):

Terminal window
cd /usr
pwd
cd share # نسبی: از /usr، پوشه‌ی share
pwd
cd /usr/share # مطلق: همان مقصد
pwd
خروجی
/usr
/usr/share
/usr/share
نوع شروع می‌شود با مثال به جای فعلی بستگی دارد؟
مطلق / /usr/share نه
نسبی هر چیز دیگر share، ../log بله
نسبت به خانه ~ ~/projects نه (از خانه)

قاعده‌ی عملی: در دستورهای تایپی کوتاه از نسبی استفاده کن؛ در اسکریپت‌ها و تنظیمات که از هر جایی ممکن است اجرا شوند، مطلق.

سه علامت ویژه‌ی مسیر:

علامت معنی
~ پوشه‌ی خانه‌ی کاربر فعلی (/home/ali)
. همین پوشه‌ی فعلی
.. یک پوشه بالاتر (والد)
Terminal window
cd ~/projects/web/css
pwd
cd ..
echo "بعد از cd .. → $(pwd)"
cd ../..
echo "بعد از cd ../.. → $(pwd)"
cd ~/projects/web/css
cd ../js
echo "به پوشه‌ی هم‌سطح (../js) → $(pwd)"
cd ../../api/v1
echo "از web/js به api/v1 (../../api/v1) → $(pwd)"
خروجی
/home/ali/projects/web/css
بعد از cd .. → /home/ali/projects/web
بعد از cd ../.. → /home/ali
به پوشه‌ی هم‌سطح (../js) → /home/ali/projects/web/js
از web/js به api/v1 (../../api/v1) → /home/ali/projects/api/v1

.. را می‌شود پشت هم زنجیر کرد (../.. دو سطح بالا). و . بیشتر موقع اجرای برنامه‌ی همین پوشه کاربرد دارد (./script.sh) و وقتی می‌خواهی بگویی «همین‌جا» (مثل cp file .).

⚡ بررسی سریع

اگر در /home/ali/projects/web/css هستی، با کدام دستور به /home/ali/projects/api می‌رسی؟

cd - به پوشه‌ی قبلی برمی‌گردد (و مسیرش را چاپ می‌کند). برای جابه‌جایی بین دو جای دور از هم ایده‌آل است:

Terminal window
cd /var/log
cd ~
echo "الان در: $(pwd)"
cd -
echo "بعد از cd - برگشتم به: $(pwd)"
cd -
echo "دوباره cd - → $(pwd)"
خروجی
الان در: /home/ali
/var/log
بعد از cd - برگشتم به: /var/log
/home/ali
دوباره cd - → /home/ali

shell پوشه‌ی قبلی را در متغیر $OLDPWD نگه می‌دارد:

Terminal window
cd /etc; cd /usr
echo "الان: $PWD | قبلی: $OLDPWD"
خروجی
الان: /usr | قبلی: /etc

بنابراین cd - همان cd "$OLDPWD" است. این همان راه‌حل تمرین درس است: از /var/log به خانه بروی و برگردی فقط با دو دستور: cd و cd -.

ls به‌تنهایی فقط اسم‌ها را می‌دهد. -l (long) اطلاعات کامل می‌دهد:

Terminal window
cd ~/projects
echo "hello" > web/index.txt
ls -l
echo "--- در پوشه‌ی web:"
ls -l web
خروجی
total 8
drwxr-xr-x 3 ali ali 4096 Oct 3 19:58 api
drwxr-xr-x 4 ali ali 4096 Oct 3 19:58 web
--- در پوشه‌ی web:
total 12
drwxr-xr-x 2 ali ali 4096 Oct 3 19:58 css
-rw-r--r-- 1 ali ali 6 Oct 3 19:58 index.txt
drwxr-xr-x 2 ali ali 4096 Oct 3 19:58 js

هر خط چند ستون دارد (توضیح کامل دسترسی‌ها در درس دسترسی‌ها):

ستون مثال معنی
۱ drwxr-xr-x نوع و دسترسی؛ d پوشه، - فایل، l لینک
۲ 4 تعداد لینک‌ها (برای پوشه‌ها: تعداد زیرپوشه‌ها + ۲)
۳ ali مالک
۴ ali گروه
۵ 4096 حجم به بایت
۶ Oct 3 19:48 زمان آخرین تغییر
۷ api نام

برای حجم خوانا از -h (human-readable) استفاده کن:

Terminal window
head -c 1500000 /dev/zero > big.bin
head -c 3000 /dev/zero > mid.bin
ls -l *.bin
echo "--- با -h:"
ls -lh *.bin
خروجی
-rw-r--r-- 1 ali ali 1500000 Oct 3 19:58 big.bin
-rw-r--r-- 1 ali ali 3000 Oct 3 19:58 mid.bin
--- با -h:
-rw-r--r-- 1 ali ali 1.5M Oct 3 19:58 big.bin
-rw-r--r-- 1 ali ali 3.0K Oct 3 19:58 mid.bin

به‌جای ۱۵۰۰۰۰۰ بایت، 1.5M می‌بینی. -lh همان -l -h است. و برای دیدن خود پوشه (نه محتوایش) از -d استفاده کن: ls -ld /var/log.

فایل یا پوشه‌ای که اسمش با . شروع شود مخفی است و ls معمولی نشانش نمی‌دهد (برای تنظیمات و فایل‌های داخلی):

Terminal window
cd ~
touch .secret-note
ls
echo "--- با -a (همه):"
ls -a
echo "--- با -A (همه بجز . و ..):"
ls -A
echo "--- با -la (بلند و همه):"
ls -la | head -8
خروجی
big.bin
docs
mid.bin
projects
--- با -a (همه):
.
..
.bash_logout
.bashrc
.profile
.secret-note
big.bin
docs
mid.bin
projects
--- با -A (همه بجز . و ..):
.bash_logout
.bashrc
.profile
.secret-note
big.bin
docs
mid.bin
projects
--- با -la (بلند و همه):
total 1500
drwxr-xr-x 4 ali ali 4096 Oct 3 19:58 .
drwxr-xr-x 1 root root 4096 Oct 3 09:43 ..
-rw-r--r-- 1 ali ali 220 Mar 31 2024 .bash_logout
-rw-r--r-- 1 ali ali 3771 Mar 31 2024 .bashrc
-rw-r--r-- 1 ali ali 807 Mar 31 2024 .profile
-rw-r--r-- 1 ali ali 0 Oct 3 19:58 .secret-note
-rw-r--r-- 1 ali ali 1500000 Oct 3 19:58 big.bin
bash: cd: /no/such/dir: No such file or directory
bash: cd: /etc/hostname: Not a directory
bash: cd: /root: Permission denied

دو ورودی ویژه همیشه هست: . (همین پوشه) و .. (والد). -A آن‌ها را حذف می‌کند. فایل‌های مخفی مهمی که می‌شناسی: .bashrc (تنظیمات shell)، .profile، .ssh/ (کلیدهای SSH در درس‌های بعد).

Terminal window
cd /no/such/dir
cd /etc/hostname
cd /root

سه پیام متفاوت: No such file or directory (مسیر وجود ندارد: غلط تایپی)، Not a directory (این یک فایل است نه پوشه) و Permission denied (وجود دارد ولی اجازه‌ی ورود نداری). هر کدام راه‌حل جدا دارد.

مثال ۹: نام با فاصله و pushd/popd

Section titled “مثال ۹: نام با فاصله و pushd/popd”

اگر اسم پوشه فاصله دارد، quote لازم است (درس shell):

Terminal window
mkdir "my project"
cd "my project"
pwd
cd ..
cd my\ project
pwd
خروجی
/home/ali/my project
/home/ali/my project

برای جابه‌جایی بین چند پوشه (نه فقط دو)، pushd و popd پشته‌ای از پوشه‌ها نگه می‌دارند:

Terminal window
pushd /etc > /dev/null
pushd /var > /dev/null
pushd /usr > /dev/null
echo "پشته: $(dirs)"
popd > /dev/null; echo "بعد از popd اول: $(pwd)"
popd > /dev/null; echo "بعد از popd دوم: $(pwd)"
خروجی
پشته: /usr /var /etc ~
بعد از popd اول: /var
بعد از popd دوم: /etc

پشت پرده: پوشه‌ی کاری یک ویژگی پروسه است

Section titled “پشت پرده: پوشه‌ی کاری یک ویژگی پروسه است”

«پوشه‌ی فعلی» چیزی نیست که روی دیسک نوشته شده باشد؛ هر پروسه (از جمله shell) در هسته یک فیلد به اسم cwd دارد. cd در shell همین فیلد را عوض می‌کند. می‌توانی ببینی:

Terminal window
cd /var/log
readlink /proc/$$/cwd
cd ~/projects
readlink /proc/$$/cwd
خروجی
/var/log
/home/ali/projects

($$ شماره‌ی پروسه‌ی shell است و /proc/$$/cwd یک لینک به پوشه‌ی کاری‌اش.) و وقتی مسیر نسبی می‌دهی (cd share)، هسته آن را با cwd ترکیب می‌کند تا مسیر مطلق بسازد. به همین دلیل cd باید داخل خود shell اجرا شود (builtin)؛ اگر برنامه‌ی جدا بود، فقط cwd فرزندش عوض می‌شد و بعد از تمام شدنش اثری نمی‌ماند. و هر برنامه‌ای که shell اجرا می‌کند cwd را از shell به ارث می‌برد؛ برای همین ls بدون argument «پوشه‌ی فعلی» را فهرست می‌کند.

دستور کار
pwd پوشه‌ی کاری فعلی (مسیر مطلق)
cd مسیر رفتن به مسیر (مطلق یا نسبی)
cd یا cd ~ رفتن به پوشه‌ی خانه
cd .. یک سطح بالا
cd - برگشت به پوشه‌ی قبلی
ls -l فهرست بلند (نوع، دسترسی، مالک، حجم، زمان)
ls -a شامل فایل‌های مخفی (با . و ..)
ls -A شامل مخفی‌ها بدون . و ..
ls -h حجم خوانا (با -l)
ls -d پوشه خودِ پوشه، نه محتوایش
pushd / popd / dirs پشته‌ی پوشه‌ها
متغیر معنی
$PWD پوشه‌ی فعلی
$OLDPWD پوشه‌ی قبلی (برای cd -)
$HOME پوشه‌ی خانه

۱) تایپ غلط یا حروف بزرگ/کوچک

Section titled “۱) تایپ غلط یا حروف بزرگ/کوچک”
Terminal window
cd /Etc
cd /etc
pwd
خروجی
/etc

/Etc با /etc فرق دارد (لینوکس به حروف کوچک/بزرگ حساس است). راه‌حل: با Tab کامل کن.

۲) فرض کردن اینکه cd چیزی چاپ می‌کند

Section titled “۲) فرض کردن اینکه cd چیزی چاپ می‌کند”

cd وقتی موفق است ساکت است. برای اطمینان pwd بزن یا پرامپت را بخوان. راه‌حل: عادت کن بعد از جابه‌جایی‌های مهم pwd بزنی.

۳) بدون quote برای اسم با فاصله

Section titled “۳) بدون quote برای اسم با فاصله”
Terminal window
mkdir "my docs"
cd my docs
خروجی
bash: cd: /Etc: No such file or directory
bash: cd: too many arguments
bash: cd: /no/such/dir: No such file or directory

shell دو argument می‌بیند (my و docs) و cd فقط یکی می‌پذیرد: too many arguments. راه‌حل: cd "my docs".

۴) رفتن به پوشه‌ای که وجود ندارد، بعد اجرای دستور خطرناک

Section titled “۴) رفتن به پوشه‌ای که وجود ندارد، بعد اجرای دستور خطرناک”
Terminal window
cd /no/such/dir && echo "این فقط وقتی اجرا می‌شود که cd موفق باشد"
echo "کد خروج cd: $?"
خروجی
کد خروج cd: 1

اگر cd شکست بخورد و دستور بعدی (مثلاً rm *) را مستقل بنویسی، روی پوشه‌ی فعلی اجرا می‌شود (نه جایی که می‌خواستی). راه‌حل: cd پوشه && دستور تا فقط در صورت موفقیت اجرا شود.

اسکریپتی که cd data می‌نویسد فقط وقتی کار می‌کند که از پوشه‌ی درست اجرا شود. راه‌حل: در اسکریپت‌ها از مسیر مطلق یا cd "$(dirname "$0")" استفاده کن (درس‌های bash).

✎ تمرینآسان

بدون هیچ آرگومانی cd بزن و نشان بده کجا هستی؛ بعد با cd - برگرد و نشان بده در کجایی.

دیدن جواب
Terminal window
cd /tmp
cd
pwd
cd -
pwd
خروجی
/home/ali
/tmp
/tmp
✎ تمرینمتوسط

تمرین اصلی: با کمترین تعداد دستور از /var/log به پوشه‌ی خانه برو و برگرد. بعد در درخت درس، از ~/projects/web/css به ~/projects/api/v1 برو (فقط با مسیر نسبی) و ثابت کن در جای درستی هستی.

دیدن جواب
Terminal window
mkdir -p projects/web/css projects/api/v1
cd /var/log
cd # خانه (یک دستور)
echo "خانه: $(pwd)"
cd - # برگشت (دستور دوم)
echo "برگشتم: $(pwd)"
cd ~/projects/web/css
cd ../../api/v1
pwd
خروجی
خانه: /home/ali
/var/log
برگشتم: /var/log
/home/ali/projects/api/v1

اولین بخش فقط دو دستور است (cd و cd -). برای بخش دوم از css دو سطح بالا (../..) می‌روی تا projects و بعد api/v1.

✎ تمرینسخت

یک تابع up بنویس که یک عدد n بگیرد و n سطح بالا برود (up 3 مثل cd ../../..). با آن از عمیق‌ترین پوشه‌ی درس، تا ریشه‌ی پروژه برگرد و نتیجه را چاپ کن.

دیدن جواب
Terminal window
mkdir -p a/b/c/d
up() { local p=""; for i in $(seq 1 "$1"); do p="../$p"; done; cd "$p" || return; }
cd a/b/c/d
echo "اینجا: $(pwd)"
up 3
echo "بعد از up 3: $(pwd)"
خروجی
اینجا: /home/ali/a/b/c/d
بعد از up 3: /home/ali/a

seq 1 3 عددهای ۱ تا ۳ را می‌دهد و حلقه سه بار ../ را به مسیر اضافه می‌کند.

؟ آزمونک
  1. تفاوت مسیر مطلق و نسبی؟

  2. cd بدون argument چه می‌کند؟

  3. .. در مسیر یعنی…

  4. cd - چه می‌کند؟

  5. کدام دستور فایل‌های مخفی را هم نشان می‌دهد؟

  6. پیام «Not a directory» بعد از cd یعنی…

  • pwd جای فعلی را می‌گوید (مسیر مطلق)؛ cd جابه‌جا می‌کند و در صورت موفقیت ساکت است.
  • مسیر مطلق از / شروع می‌شود؛ نسبی از پوشه‌ی فعلی. ~ خانه، . همین‌جا، .. یک سطح بالا.
  • cd (بدون argument) = خانه؛ cd - = پوشه‌ی قبلی؛ pushd/popd برای چند پوشه.
  • ls -l ستون‌های نوع/دسترسی، مالک، گروه، حجم، زمان و نام دارد؛ -h حجم خوانا، -a فایل‌های مخفی.
  • پوشه‌ی کاری ویژگی هر پروسه است؛ cd باید builtin باشد تا همان shell را عوض کند.
  • نام با فاصله را quote کن، و در اسکریپت‌ها مسیر مطلق بنویس یا cd پوشه && دستور را به کار ببر.
برگه‌ی تقلب این درس
دستورکاری که می‌کند
pwdپوشه‌ی فعلی
cd /var/logرفتن با مسیر مطلق
cd ..یک سطح بالا
cdرفتن به خانه
cd -برگشت به پوشه‌ی قبلی
ls -laفهرست بلند با فایل‌های مخفی
ls -lhفهرست بلند با حجم خوانا
ls -ld /var/logخود پوشه (نه محتوا)
pushd DIR / popdپشته‌ی پوشه‌ها
echo $PWD $OLDPWDپوشه‌ی فعلی و قبلی