توی این درس یاد میگیری shell برای هر برنامهای که اجرا میکند یک «کارت اطلاعات» همراهش میدهد: متغیرهای محیطی (environment variables). با env و export VAR=value آنها را میبینی و میسازی؛ میفهمی PATH چطور تعیین میکند دستور ls کجا پیدا شود؛ فرق .bashrc و .profile را میدانی و تنظیماتت را دائمی میکنی؛ با alias میانبر میسازی و با source ~/.bashrc بدون باز کردن ترمینال جدید اعمالش میکنی. تمرین: یک پوشهی اسکریپت شخصی به PATH اضافه میکنی.
مسئله: برنامهها چطور تنظیمات را از shell میگیرند؟
Section titled “مسئله: برنامهها چطور تنظیمات را از shell میگیرند؟”وقتی ls را میزنی، shell از کجا میداند آن برنامه کجاست؟ وقتی git commit ویرایشگر باز میکند، از کجا میداند چه ویرایشگری؟ وقتی یک برنامه باید بداند «پوشهی خانهی کاربر کجاست»، «زبان سیستم چیست» یا «رمز دیتابیس چیست»، از کجا بخواند؟ جواب هر سه یکی است: متغیرهای محیطی؛ جفتهای نام=مقدار که هر پروسه از والدش به ارث میبرد.
تشبیه: کارت اطلاعاتی که به هر کارمند جدید میدهند
Section titled “تشبیه: کارت اطلاعاتی که به هر کارمند جدید میدهند”وقتی مدیر (shell) یک کارمند جدید (پروسهی فرزند) استخدام میکند، یک کارت اطلاعات هم به او میدهد: «پوشهی کار: …، زبان: …، ویرایشگر: …». کارمند یک کپی از کارت را دارد: اگر چیزی رویش بنویسد، کارت مدیر عوض نمیشود؛ و اگر کارمند خودش کارمند دیگری استخدام کند، همان کارت (کپی او) را به او میدهد. نوشتن روی کارت مدیر بعد از استخدام هم به کارمندان فعلی نمیرسد.
مثالهای عملی
Section titled “مثالهای عملی”مثال ۱: متغیر shell در برابر متغیر محیطی
Section titled “مثال ۱: متغیر shell در برابر متغیر محیطی”یک متغیر ساده در shell با نام=مقدار ساخته میشود (بدون فاصله دور =) و با $نام خوانده میشود. اما فقط خود shell آن را دارد؛ برنامههای فرزند نه، مگر export شود:
NAME=Aliecho "در shell: $NAME"echo "--- فرزند (یک bash جدید) آن را میبیند؟"bash -c 'echo "فرزند: [$NAME]"'echo "--- بعد از export:"export NAMEbash -c 'echo "فرزند: [$NAME]"'echo "--- printenv و env هم آن را نشان میدهند:"printenv NAMEenv | grep '^NAME='echo "--- unset:"unset NAMEecho "بعد از unset: [$NAME]"در shell: Ali--- فرزند (یک bash جدید) آن را میبیند؟فرزند: []--- بعد از export:فرزند: [Ali]--- printenv و env هم آن را نشان میدهند:AliNAME=Ali--- unset:بعد از unset: []دو شکل export: export VAR=value (ساختن و export یکجا؛ مثل export NAME=Ali)، یا اول NAME=Ali و بعد export NAME. و قراردادی که همهی دنیا رعایت میکنند: نام متغیرهای محیطی حروف بزرگ و _ است (DB_HOST، EDITOR).
مثال ۲: env، متغیرهای مهم و محیط یک دستور
Section titled “مثال ۲: env، متغیرهای مهم و محیط یک دستور”env (یا printenv) همهی متغیرهای محیطی را چاپ میکند. چند تای مهم:
echo "--- چند متغیر استاندارد:"env | grep -E '^(HOME|USER|SHELL|LANG|PWD)='echo "--- تعداد کل متغیرهای محیط این shell: $(env | wc -l)"echo "--- مقدار یک متغیر با echo (علامت \$ و نام):"echo "پوشهی خانهی من: $HOME | کاربر: $USER"--- چند متغیر استاندارد:SHELL=/bin/bashPWD=/home/aliHOME=/home/aliLANG=C.UTF-8USER=ali--- تعداد کل متغیرهای محیط این shell: 13--- مقدار یک متغیر با echo (علامت $ و نام):پوشهی خانهی من: /home/ali | کاربر: ali| متغیر | معنی |
|---|---|
HOME |
پوشهی خانهی کاربر (همان ~) |
USER |
نام کاربر |
SHELL |
shell پیشفرض |
PWD |
پوشهی فعلی |
LANG |
زبان و رمزگذاری سیستم |
PATH |
فهرست پوشههایی که دستورها در آنها جستجو میشوند (مثال ۳) |
EDITOR |
ویرایشگر پیشفرض برنامههایی مثل git |
TZ |
منطقهی زمانی |
یک متغیر را میشود فقط برای یک دستور تنظیم کرد: VAR=value دستور. محیط shell عوض نمیشود. مثلاً منطقهی زمانی فقط برای یک date:
echo "--- ساعت سیستم:"dateecho "--- فقط برای همین یک دستور، به وقت تهران:"TZ=Asia/Tehran dateecho "--- و بعدش دوباره عادی (TZ تغییری در shell نداد):"dateecho "TZ در shell: [$TZ]"--- ساعت سیستم:Sat Oct 3 21:24:01 UTC 2026--- فقط برای همین یک دستور، به وقت تهران:Sun Oct 4 00:54:01 +0330 2026--- و بعدش دوباره عادی (TZ تغییری در shell نداد):Sat Oct 3 21:24:01 UTC 2026TZ در shell: []مثال ۳: PATH، دستور از کجا پیدا میشود؟
Section titled “مثال ۳: PATH، دستور از کجا پیدا میشود؟”وقتی ls را میزنی، shell فایلی به این نام را به ترتیب در هر پوشهی PATH (جداشده با :) میگردد و اولین مورد را اجرا میکند. مقدارش را با echo $PATH میبینی:
echo "$PATH"echo "--- هر پوشه در یک خط:"echo "$PATH" | tr ':' '\n'echo "--- دستور ls از کدام پوشه میآید؟"which lstype -a lsecho "--- یک دستور نصبنشده:"mytool/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin--- هر پوشه در یک خط:/usr/local/sbin/usr/local/bin/usr/sbin/usr/bin/sbin/bin--- دستور ls از کدام پوشه میآید؟/usr/bin/lsls is /usr/bin/lsls is /bin/ls--- یک دستور نصبنشده:bash: mytool: command not foundwhich اولین مسیر منطبق را میدهد و type -a همهی آنها را (در Ubuntu جدید /bin لینک به /usr/bin است و هر دو مسیر یک فایلاند). mytool که در هیچ پوشهی PATH نیست، command not found میدهد. حالا تمرین اصلی درس: یک پوشهی اسکریپت شخصی بساز و به PATH اضافه کن:
mkdir -p ~/binprintf '#!/bin/sh\necho "سلام از اسکریپت شخصی من"\n' > ~/bin/hellochmod +x ~/bin/helloecho "--- قبل از اضافهکردن به PATH:"helloecho "--- با مسیر کامل کار میکند:"~/bin/helloecho "--- بعد از export PATH=\"\$HOME/bin:\$PATH\":"export PATH="$HOME/bin:$PATH"hellocommand -v helloecho "$PATH" | tr ':' '\n' | head -3--- قبل از اضافهکردن به PATH:bash: hello: command not found--- با مسیر کامل کار میکند:سلام از اسکریپت شخصی من--- بعد از export PATH="$HOME/bin:$PATH":سلام از اسکریپت شخصی من/home/ali/bin/hello/home/ali/bin/usr/local/sbin/usr/local/binexport PATH="$HOME/bin:$PATH" یعنی «~/bin را به ابتدای فهرست قبلی اضافه کن». مقدار قبلی PATH را حتماً نگه دار (:$PATH آخرش) وگرنه بقیهی دستورها گم میشوند (اشتباهات رایج). ترتیب مهم است: اولین پوشهی منطبق برنده است، پس برنامهی تو میتواند دستور سیستمی را پنهان کند:
printf '#!/bin/sh\necho "من ls قلابیام!"\n' > ~/bin/lschmod +x ~/bin/lslstype -a lsrm ~/bin/lshash -recho "--- بعد از حذفش:"type -a ls | head -1من ls قلابیام!ls is /home/ali/bin/lsls is /usr/bin/lsls is /bin/ls--- بعد از حذفش:ls is /usr/bin/ls(hash -r حافظهی shell از مسیر دستورها را پاک میکند؛ shell مسیر اولین پیداشدن را در یک جدول نگه میدارد تا هر بار نگردد.) به همین دلیل پوشهی خودت را اول میگذاری فقط وقتی عمداً میخواهی دستور سیستمی را عوض کنی؛ در غیر این صورت میتوانی آخر هم بگذاری (PATH="$PATH:$HOME/bin").
مثال ۴: .profile و .bashrc، تنظیمات دائمی
Section titled “مثال ۴: .profile و .bashrc، تنظیمات دائمی”متغیری که با export در یک ترمینال ساختی با بستهشدن آن ترمینال میرود. برای دائمیشدن، آن را در یکی از فایلهای شروع (startup files) shell مینویسی که هنگام اجرا خوانده میشوند:
فایلهای پیشفرض Ubuntu را ببین. در ~/.profile یک بلوک هست که ~/bin را خودکار به PATH اضافه میکند اگر وجود داشته باشد (برای همین روی ssh بعدی تو، ~/bin خودش اضافه میشود):
echo "--- ~/.profile (بدون توضیح و خط خالی):"grep -v '^#' ~/.profile | grep -v '^$'echo "--- ~/.bashrc: خطهای اول (اگر shell تعاملی نباشد همانجا خارج میشود):"grep -v '^#' ~/.bashrc | grep -v '^$' | head -6--- ~/.profile (بدون توضیح و خط خالی):if [ -n "$BASH_VERSION" ]; then # include .bashrc if it exists if [ -f "$HOME/.bashrc" ]; then . "$HOME/.bashrc" fifiif [ -d "$HOME/bin" ] ; then PATH="$HOME/bin:$PATH"fiif [ -d "$HOME/.local/bin" ] ; then PATH="$HOME/.local/bin:$PATH"fi--- ~/.bashrc: خطهای اول (اگر shell تعاملی نباشد همانجا خارج میشود):case $- in *i*) ;; *) return;;esacHISTCONTROL=ignorebothshopt -s histappendحالا ثابت میکنم کدام فایل چه وقت خوانده میشود: در هر دو یک خط نشانه (marker) اضافه میکنم و چهار نوع shell را اجرا میکنم (-l یعنی login، -i یعنی تعاملی):
echo 'echo " [.bashrc خوانده شد]"' >> ~/.bashrcecho 'echo " [.profile خوانده شد]"' >> ~/.profileecho "--- bash -c (نه login، نه تعاملی؛ مثل اجرای یک اسکریپت):"bash -c 'true'echo "--- bash -ic (تعاملی، مثل باز کردن یک پنجرهی ترمینال):"bash -ic 'true' 2>/dev/nullecho "--- bash -lc (login ولی غیرتعاملی):"bash -lc 'true'echo "--- bash -lic (login و تعاملی، مثل ورود با ssh):"bash -lic 'true' 2>/dev/nullecho "--- نشانهها را برمیدارم:"sed -i '/خوانده شد/d' ~/.bashrc ~/.profilebash -lic 'true' 2>/dev/nullecho "(دیگر چیزی چاپ نمیشود)"--- bash -c (نه login، نه تعاملی؛ مثل اجرای یک اسکریپت):--- bash -ic (تعاملی، مثل باز کردن یک پنجرهی ترمینال): [.bashrc خوانده شد]--- bash -lc (login ولی غیرتعاملی): [.profile خوانده شد]--- bash -lic (login و تعاملی، مثل ورود با ssh): [.bashrc خوانده شد] [.profile خوانده شد]--- نشانهها را برمیدارم:(دیگر چیزی چاپ نمیشود)اسکریپتها (bash -c) هیچکدام را نمیخوانند، پنجرهی ترمینال فقط .bashrc را، و ورود با ssh هر دو را (چون .profile در انتهایش .bashrc را هم صدا میزند). قاعدهی کاربردی: export و PATH را در ~/.profile (یا ~/.bashrc) بگذار و alias و تابع را در ~/.bashrc. (شخصیسازی همهی کاربرهای سیستم: /etc/environment و /etc/profile.)
مثال ۵: alias، تابع و source
Section titled “مثال ۵: alias، تابع و source”alias یک میانبر است: alias ll='ls -la' یعنی «هر وقت ll زدم، ls -la اجرا کن». چون alias و .bashrc به shell تعاملی مربوطاند، یک ترمینال مجازی واقعی با .bashrc پیشفرض باز میکنم (مثل درسهای قبل با tmux؛ علامت ترمینال ali@pc:~$ است):
run_in_tmux() { tmux -u kill-server 2>/dev/null; tmux -u new-session -d -s t -x "${TW:-76}" -y "${TH:-24}" "$@"; sleep 1; }screen() { tmux capture-pane -t t -p | sed -e :a -e '/^[[:space:]]*$/{$d;N;ba' -e '}'; }TH=22run_in_tmux "bash"tmux send-keys -t t "alias ll='ls -la'" Enter; sleep 1tmux send-keys -t t "ll ~/bin" Enter; sleep 1tmux send-keys -t t "alias | grep -E '^alias (ll|ls)='" Enter; sleep 1tmux send-keys -t t "type ll" Enter; sleep 1tmux send-keys -t t "unalias ll" Enter; sleep 1tmux send-keys -t t "ll" Enter; sleep 1screenali@pc:~$ alias ll='ls -la'ali@pc:~$ ll ~/bintotal 16drwxr-xr-x 2 ali ali 4096 Oct 3 21:24 .drwxr-xr-x 1 ali ali 4096 Oct 3 21:24 ..-rwxr-xr-x 1 ali ali 60 Oct 3 21:24 helloali@pc:~$ alias | grep -E '^alias (ll|ls)='alias ll='ls -la'alias ls='ls --color=auto'ali@pc:~$ type llll is aliased to `ls -la'ali@pc:~$ unalias llali@pc:~$ llbash: ll: command not foundali@pc:~$alias بدون آرگومان همهی alias های فعلی را نشان میدهد (اینجا با grep فقط دو تا را گرفتم؛ .bashrc پیشفرض Ubuntu خودش چند تا ساخته، مثل ls رنگی؛ و ll را من دوباره تعریف کردم)، type ll میگوید ll یک alias است، و unalias ll برش میدارد (بعدش ll دیگر نیست). مشکل: alias فقط در همین shell تا بستهشدن آن زنده است. دائمیاش را در ~/.bashrc مینویسی. یک نکتهی اجرایی: تغییر .bashrc روی ترمینال فعلی اثر نمیگذارد، چون فقط هنگام شروع shell خوانده میشود؛ باید source بزنی (یا ترمینال تازه باز کنی):
tmux send-keys -t t "clear" Entertmux send-keys -t t "echo \"alias hi='echo سلام از alias'\" >> ~/.bashrc" Enter; sleep 1tmux send-keys -t t "hi" Enter; sleep 1tmux send-keys -t t "source ~/.bashrc" Enter; sleep 1tmux send-keys -t t "hi" Enter; sleep 1screentmux -u kill-serverali@pc:~$ echo "alias hi='echo سلام از alias'" >> ~/.bashrcali@pc:~$ hibash: hi: command not foundali@pc:~$ source ~/.bashrcali@pc:~$ hiسلام از aliasali@pc:~$قبل از source مجهول بود (hi: command not found) و بعدش کار کرد. source فایل (همان . فایل) فایل را داخل همین shell اجرا میکند. alias ها در اسکریپتها هم کار نمیکنند:
alias gs='echo من alias هستم'gsecho "--- راهحل برای کار تکرارشونده در اسکریپت: تابع"greet() { echo "سلام، $1!"; }greet Alibash: gs: command not found--- راهحل برای کار تکرارشونده در اسکریپت: تابعسلام، Ali!(در shell غیرتعاملی، alias ها بسط داده نمیشوند: command not found. برای کار پیچیدهتر از جایگزینی ساده، یک تابع در .bashrc بنویس؛ تابع آرگومان میگیرد: mkcd() { mkdir -p "$1" && cd "$1"; }.)
تغییری در ~/.bashrc دادهای. چطور آن را روی ترمینال فعلی اعمال میکنی بدون بستنش؟
source فایل را داخل همین shell اجرا میکند. با bash ~/.bashrc یک shell فرزند ساخته میشود و تغییرات با پایانش از بین میرود.
مثال ۶: source در برابر اجرای اسکریپت
Section titled “مثال ۶: source در برابر اجرای اسکریپت”فرق source با اجرای عادی را با یک فایل تنظیمات ساده ببین (همان الگوی فایلهای .env):
printf 'GREETING=salam\nexport MODE=dev\n' > app.envecho "--- اجرا مثل برنامه (فرزند):"bash app.envecho "GREETING در shell من: [$GREETING]"echo "--- source (داخل همین shell):"source app.envecho "GREETING در shell من: [$GREETING] MODE: [$MODE]"echo "--- فرزند فقط MODE را (که export شده) میبیند:"bash -c 'echo "فرزند: GREETING=[$GREETING] MODE=[$MODE]"'--- اجرا مثل برنامه (فرزند):GREETING در shell من: []--- source (داخل همین shell):GREETING در shell من: [salam] MODE: [dev]--- فرزند فقط MODE را (که export شده) میبیند:فرزند: GREETING=[] MODE=[dev]اجرای عادی در پروسهی فرزند است و متغیرهایش با پایانش میروند، پس GREETING در shell من نماند. source در همان shell اجرا میکند، پس هر دو متغیر ماندند، ولی فقط MODE که export شده به فرزند میرسد.
مثال ۷: رازها، متغیر محیطی نه خط فرمان
Section titled “مثال ۷: رازها، متغیر محیطی نه خط فرمان”رمز و توکن را روی خط فرمان نده؛ خط فرمان همهی پروسهها را هر کاربری با ps میبیند. محیط پروسه را فقط صاحب پروسه (و root) میخواند. دو پروسه با یک رمز نمایشی راه میاندازم و از دید کاربر دیگری (nobody) نگاه میکنم:
python3 -c 'import time; time.sleep(60)' --password=RAZ-KHATT &p1=$!DB_PASSWORD=RAZ-MOHIT python3 -c 'import time; time.sleep(60)' &p2=$!sleep 1echo "--- کاربر دیگر (nobody) با ps چه میبیند:"sudo -u nobody ps -o args= -p $p1,$p2echo "--- و میتواند محیط پروسهی دوم را بخواند؟"sudo -u nobody cat /proc/$p2/environecho "--- خود صاحب میتواند:"tr '\0' '\n' < /proc/$p2/environ | grep '^DB_PASSWORD='kill $p1 $p2--- کاربر دیگر (nobody) با ps چه میبیند:python3 -c import time; time.sleep(60) --password=RAZ-KHATTpython3 -c import time; time.sleep(60)--- و میتواند محیط پروسهی دوم را بخواند؟cat: /proc/1210/environ: Permission denied--- خود صاحب میتواند:DB_PASSWORD=RAZ-MOHITرمز روی خط فرمان (--password=RAZ-KHATT) برای هر کاربری با ps پیداست، ولی محیط (DB_PASSWORD) نه: nobody با Permission denied رد شد. (این رمزها نمایشیاند. در دنیای واقعی رمز را در یک فایل با دسترسی 600 یا در متغیری که از آن فایل خوانده میشود نگه دار، و در ~/.bash_history و git نگذارش.) به همین دلیل برنامههای مدرن تنظیمات را از محیط میخوانند (در درسهای داکر هم docker run -e همین است).
پشت پرده: متغیرها کجا زندگی میکنند؟
Section titled “پشت پرده: متغیرها کجا زندگی میکنند؟”محیط یک پروسه فقط رشتههایی نام=مقدار است که هسته هنگام exec در حافظهی آن میگذارد. وقتی shell برنامهای را اجرا میکند، همهی متغیرهای exportشده را بهعنوان کپی به آن میدهد (شکل بالا). هسته آنها را در /proc/PID/environ نمایش میدهد (جداشده با کاراکتر NUL):
NAME=Ali bash -c 'echo "--- محیط این پروسه (فقط چند خط، NUL را با خط جدید عوض کردم):"; tr "\0" "\n" < /proc/$$/environ | grep -E "^(NAME|HOME)="'echo "--- فرزند متغیر را عوض میکند؛ والد تغییر نمیکند:"export COLOR=bluebash -c 'COLOR=red; echo "فرزند: $COLOR"'echo "والد: $COLOR"--- محیط این پروسه (فقط چند خط، NUL را با خط جدید عوض کردم):NAME=AliHOME=/home/ali--- فرزند متغیر را عوض میکند؛ والد تغییر نمیکند:فرزند: redوالد: blueمتغیرهای shell (که export نشدهاند) فقط در حافظهی خود shell هستند و در /proc/PID/environ نمیآیند. و env را وقتی بدون آرگومان بزنی همین فهرست را چاپ میکند، و env VAR=value دستور همان VAR=value دستور است (برای وقتی که shell غیر bash باشد مفید است).
جدولهای مرجع
Section titled “جدولهای مرجع”| دستور | کار |
|---|---|
NAME=value |
متغیر shell (فقط همین shell) |
export NAME=value / export NAME |
متغیر محیطی (برای فرزندها) |
echo "$NAME" |
مقدار (همیشه داخل گیومه) |
printenv NAME / env |
نمایش محیط |
unset NAME |
حذف |
VAR=value دستور |
فقط برای همین یک دستور |
echo "$PATH" | tr ':' '\n' |
هر پوشهی PATH در یک خط |
export PATH="$HOME/bin:$PATH" |
افزودن پوشه به ابتدای PATH |
which نام / type -a نام |
کدام فایل اجرا میشود / همهی منطبقها |
hash -r |
پاککردن حافظهی مسیر دستورها |
alias ll='ls -la' / unalias ll |
میانبر |
source ~/.bashrc |
اعمال تغییر فایل شروع در همین shell |
| فایل | چه وقت | چه چیزی بنویس |
|---|---|---|
~/.profile |
shell ورود (login، مثل ssh) | export و PATH |
~/.bashrc |
هر shell تعاملی | alias، تابع، پرامپت |
/etc/environment |
ورود همهی کاربرها | متغیرهای سراسری ساده (نام=مقدار) |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) فاصله دور =
Section titled “۱) فاصله دور =”export GREETING = salamNAME = Aliecho "درست: بدون فاصله"NAME=Ali; echo "NAME=$NAME"bash: export: `=': not a valid identifierbash: NAME: command not foundدرست: بدون فاصلهNAME=Aliدر نام=مقدار فاصله نباید باشد: با فاصله، shell NAME را یک دستور میبیند و = و Ali را آرگومانهایش. راهحل: NAME=Ali.
۲) PATH را بازنویسی کردن، نه اضافهکردن
Section titled “۲) PATH را بازنویسی کردن، نه اضافهکردن”echo "--- فقط ~/bin در PATH، ls گم میشود:"( PATH=$HOME/bin; ls )echo "--- درست: PATH قبلی را نگه دار"( PATH="$HOME/bin:$PATH"; ls ~/bin )--- فقط ~/bin در PATH، ls گم میشود:bash: ls: command not found--- درست: PATH قبلی را نگه دارhelloوقتی PATH=/opt/bin (بدون $PATH) بنویسی، همهی پوشههای قبلی (/usr/bin…) حذف میشوند و حتی ls پیدا نمیشود. (اینجا داخل ( ... ) یک shell فرعی بود تا shell اصلی خراب نشود؛ اگر در .bashrc این اشتباه را بکنی، هر ترمینال تازه خراب شروع میشود و باید با /usr/bin/nano ~/.bashrc و مسیر کامل درستش کنی.) راهحل: همیشه PATH="…:$PATH" یا PATH="$PATH:…".
۳) فراموشکردن export
Section titled “۳) فراموشکردن export”برنامهی فرزند متغیر را نمیبیند (مثال ۱). علامتش: «در shell خودم echo $VAR درست است ولی برنامه میگوید تنظیم نشده». راهحل: export VAR.
۴) متغیر بدون گیومه با فاصله یا *
Section titled “۴) متغیر بدون گیومه با فاصله یا *”mkdir -p ex4 && cd ex4FILE="my notes.txt"echo "--- بدون گیومه: دو آرگومان!"touch $FILElsrm -f my notes.txtecho "--- با گیومه: یک آرگومان"touch "$FILE"lscd .. && rm -rf ex4--- بدون گیومه: دو آرگومان!mynotes.txt--- با گیومه: یک آرگومانmy notes.txtshell متغیر بدون گیومه را بعد از جایگزینی به کلمهها میشکند؛ touch $FILE دو فایل my و notes.txt ساخت. راهحل: همیشه "$FILE".
۵) انتظار اعمال فوری تغییر .bashrc
Section titled “۵) انتظار اعمال فوری تغییر .bashrc”مثال ۵: تغییر فایل روی ترمینال باز تأثیری ندارد تا source یا ترمینال تازه. راهحل: source ~/.bashrc.
۶) alias در اسکریپت
Section titled “۶) alias در اسکریپت”مثال ۵: gs: command not found. راهحل: تابع یا اسکریپت مستقل در ~/bin.
مقدار HOME، USER و SHELL را با echo چاپ کن و با printenv هم یکی را بخوان.
دیدن جواب
echo "HOME=$HOME USER=$USER SHELL=$SHELL"printenv HOMEHOME=/home/ali USER=ali SHELL=/bin/bash/home/aliتمرین اصلی: یک پوشهی اسکریپت شخصی به PATH اضافه کن، دائمی. پوشهی ~/tools را بساز، اسکریپت now (که تاریخ و ساعت را چاپ میکند) را در آن بگذار، خط export PATH را در ~/.bashrc بنویس، و با یک shell تعاملی تازه (bash -ic) ثابت کن now بدون مسیر کامل کار میکند.
دیدن جواب
mkdir -p ~/toolsprintf '#!/bin/sh\ndate "+امروز %%F ساعت %%T"\n' > ~/tools/nowchmod +x ~/tools/nowecho 'export PATH="$HOME/tools:$PATH"' >> ~/.bashrcecho "--- shell فعلی هنوز نمیداند:"nowecho "--- shell تعاملی تازه (مثل باز کردن ترمینال جدید):"bash -ic 'now; command -v now' 2>/dev/null--- shell فعلی هنوز نمیداند:bash: now: command not found--- shell تعاملی تازه (مثل باز کردن ترمینال جدید):امروز 2026-10-03 ساعت 21:24:14/home/ali/tools/nowدر ~/.bashrc یک alias (ll) و یک تابع (mkcd: پوشه بساز و واردش شو) و یک متغیر محیطی (EDITOR=nano) بنویس و در یک shell تعاملی تازه نشان بده هر سه کار میکنند.
دیدن جواب
cat >> ~/.bashrc <<'EOF'alias ll='ls -la'mkcd() { mkdir -p "$1" && cd "$1"; }export EDITOR=nanoEOFbash -ic 'type ll; mkcd /tmp/lx-new-dir && pwd; echo "EDITOR=$EDITOR"; cd /; rmdir /tmp/lx-new-dir' 2>/dev/nullll is aliased to `ls -la'/tmp/lx-new-dirEDITOR=nanoآزمونک
Section titled “آزمونک”فرق NAME=Ali و export NAME=Ali؟
فقط متغیرهای exportشده بهصورت کپی به پروسههای فرزند ارث میرسند.
PATH چه کاری میکند؟
اولین فایل اجرایی منطبق در پوشههای PATH اجرا میشود.
درستترین راه برای افزودن ~/bin به PATH؟
مقدار قبلی را باید نگه داشت؛ وگرنه ls هم پیدا نمیشود.
کدام فایل برای هر پنجرهی ترمینال تازه (shell تعاملی) خوانده میشود و جای مناسب alias است؟
.profile برای shell ورود (login) است؛ در Ubuntu آن هم .bashrc را صدا میزند.
تغییری در ~/.bashrc دادهای. چطور بدون ترمینال جدید اعمالش میکنی؟
source فایل را داخل همین shell اجرا میکند؛ bash فایل یک فرزند میسازد که با پایانش تغییرات میرود.
چرا رمز را بهجای خط فرمان در متغیر محیطی میدهیم؟
البته خود دستور export با رمز در history میماند؛ بهتر است رمز را از فایلی با دسترسی 600 بخوانی.
جمعبندی
Section titled “جمعبندی”- متغیر محیطی: جفتهای
نام=مقدارکه پروسه به فرزندانش میدهد (کپی).NAME=vفقط shell،export NAME=vبرای فرزندها.env/printenvفهرست میکنند وVAR=v دستورفقط برای یک دستور است. بدون فاصله دور=، و"$NAME"همیشه داخل گیومه. PATH: پوشههای جستجوی دستور (با:)، اولین منطبق برنده.export PATH="$HOME/bin:$PATH"وtype -aبرای دیدن همهی منطبقها.- ماندگاری:
~/.profile(shell ورود) و~/.bashrc(هر shell تعاملی)؛source ~/.bashrcاعمال فوری. aliasبرای میانبر در shell تعاملی؛ برای کار بیشتر تابع در.bashrc(alias در اسکریپت کار نمیکند).sourceدر همین shell اجرا میکند؛ اجرای عادی اسکریپت یک فرزند است.- رازها: در محیط یا فایل با دسترسی ۶۰۰، نه روی خط فرمان (که با
psپیداست).
| دستور | کاری که میکند |
|---|---|
NAME=value / export NAME=value | متغیر shell / محیطی |
echo "$NAME" / printenv NAME / env | خواندن و فهرست |
unset NAME | حذف |
VAR=value دستور | فقط برای یک دستور (مثلاً TZ=Asia/Tehran date) |
echo "$PATH" | tr ":" "\n" | پوشههای PATH |
export PATH="$HOME/bin:$PATH" | افزودن پوشه به PATH |
which نام / type -a نام | کدام فایل اجرا میشود |
alias ll='ls -la' / unalias ll | میانبر |
source ~/.bashrc | اعمال تغییر بدون ترمینال جدید |
bash -ic 'دستور' | آزمایش در یک shell تعاملی تازه |
~/.profile / ~/.bashrc | ماندگاری: shell ورود / هر ترمینال |