۱۳ – سفارشی‌سازی اعلان (Prompt)

در این فصل به یک جزئیات ظاهراً پیش‌پاافتاده می‌پردازیم — یعنی اعلان پوسته (Shell Prompt).
اما همین موضوع ساده، ما را با بخشی از سازوکار درونی پوسته و برنامه‌ی شبیه‌ساز ترمینال آشنا می‌کند.

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

ساختار یک اعلان

اعلان پیش‌فرض ما معمولاً چیزی شبیه به این است:

[me@linuxbox ~]$

توجه کنید که شامل نام کاربر، نام میزبان (hostname) و مسیر کاری فعلی است.
اما این قالب از کجا آمده؟
خیلی ساده: این قالب توسط یک متغیر محیطی به نام PS1 (مخفف Prompt String 1) تعریف می‌شود.

برای دیدن مقدار آن می‌توانیم از دستور زیر استفاده کنیم:

[me@linuxbox ~]$ echo $PS1
[\u@\h \W]\$

نکته: نگران نباشید اگر خروجی شما دقیقاً مثل مثال بالا نبود.
هر توزیع لینوکسی قالب اعلان را کمی متفاوت تعریف می‌کند — بعضی حتی به شکل‌های خاص و عجیب.

از نتیجه مشخص است که PS1 شامل چند کاراکتر معمول مثل براکت‌ها، علامت @ و علامت دلار است،
اما بقیه‌ی آن مبهم به نظر می‌رسد.
اگر دقت کرده باشید، این‌ها کاراکترهای خاصی هستند که با بک‌اسلش (\) شروع می‌شوند —
شبیه همان مواردی که در فصل ۷ دیدیم.

در جدول زیر فهرستی از این کاراکترهای خاص آورده شده که پوسته در رشته‌ی اعلان به شکل ویژه تفسیر می‌کند:


جدول 13-1: کدهای escape مورد استفاده در اعلان پوسته

توالی مقدار نمایشی توضیح
\a زنگ ASCII باعث بوق زدن سیستم می‌شود.
\d تاریخ فعلی در قالب "روز، ماه، عدد روز" (مثل: Mon May 26)
\h نام میزبان سیستم (بدون بخش دامنه)
\H نام کامل میزبان
\j تعداد jobهای در حال اجرا در نشست فعلی پوسته
\l نام دستگاه ترمینال فعلی
\n نویسه‌ی خط جدید (newline)
\r بازگشت به ابتدای خط (carriage return)
\s نام برنامه‌ی پوسته
\t زمان فعلی در قالب ۲۴ ساعته (ساعت:دقیقه:ثانیه)
\T زمان فعلی در قالب ۱۲ ساعته
\@ زمان فعلی در قالب ۱۲ ساعته همراه با AM/PM
\A زمان فعلی در قالب ۲۴ ساعته (ساعت:دقیقه)
\u نام کاربر فعلی
\v شماره نسخه‌ی پوسته
\V شماره نسخه و انتشار پوسته
\w مسیر کامل پوشه‌ی کاری فعلی
\W فقط نام آخرین پوشه از مسیر کاری فعلی
\! شماره‌ی تاریخی (History number) فرمان فعلی
\# تعداد کل فرمان‌هایی که در این نشست وارد شده
\$ اگر کاربر معمولی باشید «$»، و اگر superuser باشید «#» نمایش می‌دهد.
\[ شروع رشته‌ای از نویسه‌های غیرقابل‌چاپ؛ برای درج کدهای کنترلی مثل تغییر رنگ یا جابه‌جایی مکان‌نما استفاده می‌شود.
\] پایان رشته‌ی نویسه‌های غیرقابل‌چاپ

آزمایش طراحی‌های مختلف برای اعلان (Prompt)

با استفاده از فهرست کاراکترهای خاصی که در بخش قبل دیدیم، می‌توانیم اعلان را تغییر دهیم تا تأثیر هرکدام را ببینیم.
اول از همه بهتر است نسخه‌ی فعلی اعلان را پشتیبان بگیریم تا هر زمان خواستیم، بتوانیم به حالت اولیه برگردیم.

[me@linuxbox ~]$ ps1_old="$PS1"

در اینجا یک متغیر جدید به نام ps1_old ساختیم و مقدار متغیر PS1 را در آن کپی کردیم.
می‌توانیم با دستور زیر بررسی کنیم که مقدار به‌درستی ذخیره شده است:

[me@linuxbox ~]$ echo $ps1_old
[\u@\h \W]\$

حالا در هر زمان از نشست فعلی ترمینال، برای بازگرداندن اعلان اصلی کافی است این دستور را بزنیم:

[me@linuxbox ~]$ PS1="$ps1_old"

حذف کامل اعلان

اگر رشته‌ی اعلان را خالی بگذاریم:

[me@linuxbox ~]$ PS1=

در این صورت هیچ اعلان متنی نمایش داده نمی‌شود. اعلان هنوز وجود دارد، اما چیزی چاپ نمی‌کند — همان‌طور که خواستیم.
از آن‌جا که این وضعیت کمی گیج‌کننده است، بهتر است یک اعلان ساده‌تر بسازیم:

PS1="\$ "

بهتر شد — حالا حداقل می‌توانیم محل ورود فرمان را ببینیم.
توجه کنید که درون کوتیشن‌ها یک فاصله بعد از $ وجود دارد تا بین نشان دلار و مکان‌نما فاصله بیفتد.


افزودن صدا (Bell) به اعلان

PS1="\[\a\]\$ "

اکنون هر بار که اعلان نمایش داده می‌شود، صدای بوق می‌شنویم.
ممکن است آزاردهنده باشد، ولی می‌تواند مفید باشد اگر بخواهیم مثلاً پس از اجرای یک دستور طولانی، متوجه اتمام آن شویم.
چون کد بوق (\a) چیزی چاپ نمی‌کند و مکان‌نما را جابجا نمی‌کند، باید با \[ ... \] مشخص کنیم که این بخش از نوع "غیرچاپی" است تا Bash بتواند طول دقیق اعلان را درست محاسبه کند.


افزودن نام میزبان و زمان

$ PS1="\A \h \$ "
17:33 linuxbox $

حالا اعلان شامل ساعت و نام میزبان است؛ مفید برای زمانی که می‌خواهیم زمان اجرای فرمان‌ها را بدانیم.


ساختن یک اعلان شبیه نسخه‌ی اصلی

17:37 linuxbox $ PS1="<\u@\h \W>\$ "
<me@linuxbox ~>$

حالا اعلان ما ظاهر اصلی خودش را دارد اما در قالب دلخواه ما.
با سایر توالی‌های escape در جدول قبلی بازی کنید و سعی کنید یک اعلان خلاقانه و کاربردی بسازید.


افزودن رنگ به اعلان

اکثر برنامه‌های ترمینال به توالی‌هایی از کاراکترهای غیرچاپی پاسخ می‌دهند که ظاهر متن را کنترل می‌کنند — مثلاً رنگ، بولد بودن یا حتی چشمک‌زن بودن متن.
در ادامه به رنگ‌ها می‌پردازیم.


یادداشتی درباره‌ی «سردرگمی ترمینال‌ها»

در گذشته، وقتی ترمینال‌ها به‌صورت فیزیکی به کامپیوترهای راه‌دور متصل می‌شدند، هر برند دستورهای مخصوص خودش را برای کنترل نمایش داشت.
برای مدیریت این هرج‌ومرج، یونیکس دو سیستم پیچیده به نام‌های termcap و terminfo را توسعه داد.
بعدها استانداردی به‌نام ANSI معرفی شد تا زبان مشترکی برای کنترل نمایش بین ترمینال‌ها ایجاد شود (همان که کاربران DOS قدیمی با فایل ANSI.SYS می‌شناختند).


کنترل رنگ‌ها با کدهای ANSI

رنگ متن از طریق ارسال «کد Escape» به ترمینال کنترل می‌شود.
این کد چاپ نمی‌شود، بلکه ترمینال آن را به‌عنوان دستور تفسیر می‌کند.

هر کد Escape با \033 (کد هشت‌هشتی کلید Escape) شروع می‌شود، سپس تنظیمات (attribute) و دستور موردنظر می‌آید.

برای مثال، این کد رنگ متن را به مشکی معمولی تنظیم می‌کند:

\033[0;30m

در جدول زیر رنگ‌های متنی موجود فهرست شده‌اند:


جدول 14-2: توالی‌های escape برای تنظیم رنگ متن

توالی رنگ متن توالی رنگ متن
\033[0;30m سیاه \033[1;30m خاکستری تیره
\033[0;31m قرمز \033[1;31m قرمز روشن
\033[0;32m سبز \033[1;32m سبز روشن
\033[0;33m قهوه‌ای \033[1;33m زرد
\033[0;34m آبی \033[1;34m آبی روشن
\033[0;35m بنفش \033[1;35m بنفش روشن
\033[0;36m فیروزه‌ای \033[1;36m فیروزه‌ای روشن
\033[0;37m خاکستری روشن \033[1;37m سفید

ساخت اعلان قرمز

<me@linuxbox ~>$ PS1="\[\033[0;31m\]<\u@\h \W>\$ "

اما در این حالت همه‌ی متن بعد از اعلان هم قرمز می‌شود.
برای بازگرداندن رنگ به حالت عادی، باید در انتهای اعلان کد ریست رنگ (\033[0m) را اضافه کنیم:

<me@linuxbox ~>$ PS1="\[\033[0;31m\]<\u@\h \W>\$\[\033[0m\] "

حالا همه چیز درست کار می‌کند.


رنگ پس‌زمینه

کدهای زیر برای تنظیم رنگ پس‌زمینه استفاده می‌شوند (ویژگی بولد در پس‌زمینه کاربرد ندارد):

توالی رنگ پس‌زمینه توالی رنگ پس‌زمینه
\033[0;40m مشکی \033[0;44m آبی
\033[0;41m قرمز \033[0;45m بنفش
\033[0;42m سبز \033[0;46m فیروزه‌ای
\033[0;43m قهوه‌ای \033[0;47m خاکستری روشن

برای مثال، ساخت اعلان با پس‌زمینه‌ی قرمز:

<me@linuxbox ~>$ PS1="\[\033[0;41m\]<\u@\h \W>\$\[\033[0m\] "

نکته

علاوه بر حالت‌های عادی (۰) و بولد (۱)، می‌توان ویژگی‌های زیر را نیز اعمال کرد:

البته بیشتر ترمینال‌ها از حالت چشمک‌زن پشتیبانی نمی‌کنند، چون واقعاً آزاردهنده است.

جابجا کردن مکان‌نما (Cursor)

کدهای Escape می‌توانند برای تعیین موقعیت مکان‌نما روی صفحه استفاده شوند.
این ویژگی معمولاً برای نمایش ساعتی کوچک یا اطلاعات دیگر در گوشه‌ی بالایی ترمینال (مثلاً کنار اعلان) به کار می‌رود.

در جدول زیر فهرست کدهایی آمده که برای جابجایی مکان‌نما استفاده می‌شوند:


جدول 13-4: توالی‌های Escape برای حرکت مکان‌نما

کد Escape عملکرد
\033[l;cH مکان‌نما را به خط l و ستون c منتقل می‌کند.
\033[nA مکان‌نما را n خط به بالا می‌برد.
\033[nB مکان‌نما را n خط به پایین می‌برد.
\033[nC مکان‌نما را n کاراکتر به جلو می‌برد.
\033[nD مکان‌نما را n کاراکتر به عقب می‌برد.
\033[2J کل صفحه را پاک کرده و مکان‌نما را به گوشه‌ی بالا-چپ (خط ۰، ستون ۰) می‌برد.
\033[K از موقعیت فعلی مکان‌نما تا انتهای خط را پاک می‌کند.
\033[s موقعیت فعلی مکان‌نما را ذخیره می‌کند.
\033[u موقعیت ذخیره‌شده‌ی مکان‌نما را بازیابی می‌کند.

اکنون با استفاده از این کدها یک اعلان می‌سازیم که در بالای صفحه یک نوار قرمز نمایش دهد و ساعت فعلی را (با متن زرد رنگ) درون آن بنویسد.
هر بار که اعلان ظاهر شود، این نوار و ساعت نیز مجدداً ترسیم می‌شوند.

PS1="\[\033[s\033[0;0H\033[0;41m\033[K\033[1;33m\t\033[0m\033[u\] <\u@\h \W>\$ "

تجزیه و توضیح هر بخش از رشته‌ی بالا

توالی عملکرد
\[ آغاز بخش غیرچاپی. برای اینکه Bash بتواند طول واقعی اعلان را درست محاسبه کند.
\033[s موقعیت فعلی مکان‌نما را ذخیره می‌کند (برای بازگشت به محل اعلان بعد از رسم نوار). توجه کنید که برخی ترمینال‌ها از این کد پشتیبانی نمی‌کنند.
\033[0;0H مکان‌نما را به گوشه‌ی بالا-چپ صفحه می‌برد (خط ۰، ستون ۰).
\033[0;41m رنگ پس‌زمینه را قرمز می‌کند.
\033[K از محل فعلی مکان‌نما تا انتهای خط را پاک می‌کند (و چون رنگ پس‌زمینه قرمز است، خط قرمز می‌شود).
\033[1;33m رنگ متن را زرد می‌کند.
\t ساعت فعلی را نمایش می‌دهد. (درون بخش غیرچاپی آمده تا Bash اندازه‌ی ساعت را در طول اعلان حساب نکند.)
\033[0m بازگرداندن تنظیمات رنگ به حالت عادی.
\033[u بازگرداندن مکان‌نما به محل ذخیره‌شده‌ی قبلی.
\] پایان بخش غیرچاپی.
<\u@\h \W>\$ متن اعلان واقعی (کاربر، میزبان و مسیر کاری فعلی).

ذخیره‌سازی اعلان

بدیهی است که هیچ‌کس نمی‌خواهد هر بار این رشته‌ی طولانی را تایپ کند.
برای دائمی کردن آن، کافی است خطوط زیر را به فایل ~/.bashrc خود اضافه کنید:

PS1="\[\033[s\033[0;0H\033[0;41m\033[K\033[1;33m\t\033[0m\033[u\] <\u@\h \W>\$ "
export PS1

جمع‌بندی

باور کنید یا نه، هنوز امکانات بسیار بیشتری برای شخصی‌سازی اعلان وجود دارد — از استفاده‌ی توابع و اسکریپت‌ها گرفته تا ساخت اعلان‌های پویا.
اما آنچه تا اینجا یاد گرفتید، پایه‌ای قدرتمند برای شروع است.
بسیاری از کاربران همان اعلان پیش‌فرض را کافی می‌دانند،
اما برای کسانی که دوست دارند دست‌کاری کنند، دنیای Bash پر از ساعت‌ها سرگرمی و خلاقیت بی‌پایان است.


منابع بیشتر