رفع خطای unknown terminal type در SSH
اگر با خطای unknown terminal type در SSH مواجه شدید، مشکل از نبود دیتابیس terminfo روی سرور است. با کامپایل فایل terminfo در دایرکتوری home بدون نیاز به دسترسی root این مشکل را حل کنید.
معنای خطای "unknown terminal type"
خطای unknown terminal type هنگام اتصال از طریق SSH به این معناست که نام موجود در TERM هیچ ورودی متناظری در دیتابیس terminfo روی سرور ندارد. شبیهساز ترمینال شما این نام را روی لپتاپتان تنظیم میکند، اما دیتابیسی که باید آن را شناسایی کند روی ماشین مقصد قرار دارد. زمانی که این جستجو با شکست مواجه میشود، تمام برنامههای مبتنی بر curses روی آن سرور پیش از ترسیم هر چیزی متوقف میشوند، زیرا هیچ توصیفی از قابلیتهای ترمینال شما در اختیار ندارند.
راهکاری که تمام ویژگیهای شبیهساز شما را حفظ میکند، اجرای یک دستور است: ورودی terminfo خود را در ~/.terminfo روی سرور کامپایل کنید. کتابخانه ncurses پیش از هر دایرکتوری سیستمی، دایرکتوری home شما را جستجو میکند؛ بنابراین این کار نیازی به دسترسی root ندارد و هیچ تغییری برای سایر کاربران ایجاد نمیکند.
TERM یک نام است و terminfo پایگاه داده آن
TERM تنها یک رشته را در خود نگه میدارد و آن رشته صرفاً یک کلید است. terminfo یک پایگاه داده کامپایلشده از توصیفات ترمینال است و این کلید، روشی است که برنامه از طریق آن، توصیف مناسب را پیدا میکند. یک توصیف مشخص میکند که ترمینال چند رنگ دارد، چه بایتهایی مکاننما را به سطر 12 و ستون 40 میبرند، کلید F5 چه بایتهایی ارسال میکند و آیا قابلیت رسم خط زیرین (underline) با استایل خاص را دارد یا خیر. کتابخانه ncurses که تقریباً تمام رابطهای کاربری متنی (TUI) در لینوکس بر پایه آن ساخته شدهاند، این توصیف را هنگام شروع به کار میخواند. برنامههایی مانند top، less، nano و watch بدون آن نمیتوانند حتی یک صفحه را ترسیم کنند.
ورودی خود را بررسی کنید:
echo "$TERM"
tput longname
infocmp -1 | head -20tput longname توصیف قابلفهم برای انسان از هر چیزی که TERM نامگذاری میکند را چاپ میکند. برای xterm-256color خروجی به این صورت است:
xterm with 256 colorsinfocmp -1 کل ورودی را دیکامپایل کرده و هر قابلیت را در یک خط چاپ میکند. اگر هر دو دستور کار کنند، پایگاه داده روی این ماشین، این ترمینال را میشناسد. این همان وضعیتی است که باید در سمت دیگر یک اتصال SSH نیز برقرار باشد.
چرا این مشکل فقط از طریق SSH رخ میدهد
SSH فقط نام را ارسال میکند و هیچ چیز دیگری همراه آن نیست. هنگامی که کلاینت درخواست یک pseudo terminal میکند، TERM همراه با آن درخواست ارسال میشود. راهنمای OpenSSH صراحتاً بیان میکند: "متغیر محیطی TERM همیشه هنگام درخواست pseudo-terminal ارسال میشود، زیرا پروتکل به آن نیاز دارد". بنابراین، shell ورود شما در VPS مقدار TERM=xterm-ghostty، یا xterm-kitty، یا هر نام دیگری که شبیهساز شما استفاده میکند را دریافت میکند. توصیفات مربوط به آن نام در لپتاپ شما و در پایگاه داده محلی آن باقی میماند. سروری که پیش از ساختهشدن شبیهساز شما ایجاد شده است، هرگز نام آن را نشنیده است. هیچ چیزی در خودِ اتصال SSH توصیفات ترمینال را کپی نمیکند و این یک تصمیم عمدی است: پروتکل فقط یک نام را حمل میکند، نه مجموعهای از قابلیتها را.
خطا را روی دستگاه خود با نامی که قطعاً وجود ندارد بازتولید کنید:
TERM=xterm-nosuchthing tput colors
echo "$?"tput: unknown terminal "xterm-nosuchthing"وضعیت خروج 3 است که ncurses آن را به عنوان نوع ترمینال ناشناخته یا پایگاه داده مفقود مستند کرده است. هر برنامه curses همان جستجو را انجام میدهد، بنابراین یک نام مفقود باعث ایجاد پیامهای متفاوتی از برنامههای مختلف میشود که همگی یک علت واحد دارند. nano با خطای Error opening terminal: xterm-nosuchthing. متوقف میشود که خطای استاندارد ncurses هنگام عدم توانایی در بارگذاری توصیفات در زمان شروع است. vim عبارت E558: Terminal entry not found in terminfo را چاپ میکند. less عبارت WARNING: terminal is not fully functional را چاپ میکند. یک pager که نیمهکاره کار میکند و یک ویرایشگر که از اجرا امتناع میورزد، ممکن است به عنوان باگهای بیارتباط به نظر برسند. آنها در واقع یک فایل مفقود مشابه هستند.
محل جستجوی ncurses برای ورودی
کتابخانه ncurses با ترتیبی ثابت جستجو میکند که در terminfo(5) مستند شده است:
$TERMINFO، در صورتی که تنظیم شده باشد. فقط همین دایرکتوری جستجو میشود.$HOME/.terminfo.- هر دایرکتوری که در
$TERMINFO_DIRSفهرست شده باشد. - مکانهای کامپایلشده در برنامه که در Debian و Ubuntu شامل
/etc/terminfo،/lib/terminfoو/usr/share/terminfoهستند.
بهجای اعتماد به مسیرهای بالا، دستور infocmp -D را روی سرور اجرا کنید تا فهرستی که نسخه ncurses همان سیستم استفاده میکند، چاپ شود. مرحله 2 تمام نکته کار است. دایرکتوری home شما پیش از هر دایرکتوری سیستمی جستجو میشود، بنابراین یک فایل در ~/.terminfo مشکل جستجو را برای حساب کاربری شما بدون نیاز به نصب پکیج یا دسترسی root برطرف میکند.
رفع 1: استفاده از یک TERM که سرور از قبل میشناسد
سریعترین راه برای رفع این مسدودیت، نامگذاری یک ترمینال متفاوت برای یک اتصال خاص است:
TERM=xterm-256color ssh user@203.0.113.10برای دائمی کردن این تنظیم برای آن میزبان در ~/.ssh/config روی لپتاپ خود، این کار را انجام دهید:
Host vps1
HostName 203.0.113.10
User deploy
SetEnv TERM=xterm-256colorSetEnv به OpenSSH نسخه 7.8 یا جدیدتر در سمت کلاینت نیاز دارد که در اوت 2018 منتشر شد. TERM تنها متغیری است که نیازی به اجازه سرور ندارد: مستندات میگویند «مشابه SendEnv، با این استثنا که برای متغیر TERM، سرور باید آماده پذیرش متغیر محیطی باشد». بنابراین این روش حتی در برابر یک sshd سختگیرانه که هیچ خط AcceptEnv ندارد نیز کار میکند.
هزینه این کار: از دست دادن تمام قابلیتهایی که شبیهساز شما فراتر از xterm-256color ساده ارائه میدهد. این شامل رنگهای 24 بیتی که از طریق قابلیت RGB معرفی میشوند و خطکشیدنهای استایلدار از طریق Smulx است. برنامهها دیگر نمیتوانند این قابلیتها را ببینند، بنابراین به 256 رنگ و خطکشیدن ساده بازمیگردند. صفحه نمایش کار میکند، اما صرفاً قابلیتهای کمتری نسبت به ترمینالی که انتخاب کردهاید دارد.
این مشکل را با افزودن export TERM=xterm-256color به .bashrc در سرور حل نکنید. آن فایل برای هر اتصال اجرا میشود، از جمله اتصالاتی که از ترمینالی با نام شناختهشده برای سرور برقرار شدهاند؛ بنابراین باعث افت کیفیت نشستهایی میشود که از قبل مشکلی نداشتند. این تغییر را در سمت کلاینت نگه دارید، جایی که میدانید از چه شبیهسازی استفاده میکنید.
راهکار 2: ورودی terminfo خود را یکبار به سرور ارسال کنید
این راهکار، شبیهساز ترمینال شما را دستنخورده باقی میگذارد. ورودی را بهصورت محلی Decompile کنید، آن را از طریق SSH منتقل کرده و در مقصد Compile کنید:
infocmp -x | ssh user@203.0.113.10 -- tic -x -infocmp -x ورودی مربوط به TERM فعلی شما را به عنوان متن منبع، شامل قابلیتهای توسعهیافته (تعریفشده توسط کاربر)، چاپ میکند. tic -x آن منبع را در سرور به یک توصیف باینری تبدیل میکند. استفاده از -x در هر دو سمت ضروری است؛ زیرا بدون آن، قابلیتهایی مانند Smulx در حین انتقال حذف میشوند و شما نسخهای تنزلیافته از ترمینال خود را ارسال خواهید کرد.
tic در صورت امکان در دایرکتوری سیستم مینویسد و اگر کاربر دسترسی نوشتن نداشته باشد، به $HOME/.terminfo بازمیگردد. یک حساب کاربری معمولی در VPS از دایرکتوری home استفاده میکند که همان چیزی است که شما نیاز دارید. همچنین میتوانید مقصد را نامگذاری کنید:
infocmp -x | ssh user@203.0.113.10 -- tic -x -o '~/.terminfo' /dev/stdinکوتیشنهای اطراف ~/.terminfo صرفاً تزئینی نیستند. ssh کلمات دستور را به یک رشته واحد تبدیل کرده و به shell راه دور تحویل میدهد؛ بنابراین اگر ~ بدون کوتیشن باشد، ابتدا توسط shell محلی شما بسط داده میشود. در نتیجه، شما یک /home/yourname/.terminfo تحتاللفظی به ماشینی میفرستید که ممکن است نام حساب کاربری شما در آن متفاوت باشد. tic با خطای مجوز مواجه میشود یا در مسیری مینویسد که ncurses هرگز آن را جستجو نمیکند. با کوتیشن گذاشتن دور tilde، اجازه دهید shell راه دور آن را بسط دهد.
وارد شوید و بررسی کنید:
ssh user@203.0.113.10
tput longname
ls -R ~/.terminfotput longname که توصیف ترمینال شما را چاپ میکند، به این معنی است که جستجو اکنون موفقیتآمیز بوده است و ls فایل کامپایلشده را در زیرشاخهای که با اولین حرف نام ترمینال نامگذاری شده، نشان میدهد. سپس top یا watch -n1 uptime که بازترسیم را از طریق همان کتابخانه انجام میدهد را اجرا کنید تا تأیید شود که یک برنامه curses واقعی، صفحه را بهدرستی ترسیم میکند.
هزینه این کار: یک مرحله برای هر سرور، و ورودی در دایرکتوری home یک حساب کاربری روی یک ماشین باقی میماند. اگر VPS را بازسازی کنید، این ورودی از بین میرود. این دستور را در ابزاری که برای provisioning و مدیریت همزمان چندین سرور استفاده میکنید قرار دهید تا هر میزبان جدید، از همان ابتدا با ورودی کامپایلشده آماده باشد.
راهکار 3: نصب پایگاه داده extended terminfo
اگر دسترسی root دارید و میخواهید نام ترمینال برای تمام حسابهای کاربری روی سیستم شناسایی شود:
sudo apt update
sudo apt install ncurses-term
tput -T xterm-ghostty longnameبسته ncurses-term شامل توصیفات ترمینالی است که در نصب پایه وجود ندارند. این کار مشکل نام ترمینال را برای root و سایر کاربران برطرف میکند، کاری که راهکار 2 قادر به انجام آن نیست.
این روش تنها زمانی کارساز است که ورودی ترمینال شما در نسخه ncurses موجود در توزیع لینوکسیتان گنجانده شده باشد؛ بنابراین بهجای فرض کردن، آن را بررسی کنید. توزیع Ubuntu 24.04 شامل نسخه ncurses-bin 6.4+20240113-1ubuntu2.1 است (تا اوت 2026). ورودی Ghostty در نسخه ncurses 6.5-20241228 به بالادست (upstream) اضافه شده است که جدیدتر است؛ بنابراین apt install ncurses-term روی Ubuntu 24.04 نمیتواند ورودی xterm-ghostty تولید کند، فارغ از اینکه چند بار آن را اجرا کنید. دستور tput -T <name> longname برای بررسی است: اگر ورودی موجود باشد، توصیفات آن را چاپ میکند و در غیر این صورت با کد خروج 3، دستور unknown terminal را اجرا میکند.
برای نصب یک ورودی تکی بهصورت سیستمی (بهجای نصب کل بسته)، فایل منبع را کپی کرده و بهعنوان root آن را کامپایل کنید:
infocmp -x | ssh user@203.0.113.10 -- 'cat > /tmp/term.src'
ssh -t user@203.0.113.10 -- sudo tic -x -o /usr/share/terminfo /tmp/term.srcاستفاده از -t در دستور دوم، یک pseudo terminal اختصاص میدهد تا sudo بتواند درخواست رمز عبور کند. بدون این سوئیچ، ممکن است sudo با خطای sudo: no tty present and no askpass program specified متوقف شود.
اجازه دهید شبیهساز انتقال را انجام دهد
برخی از شبیهسازها ابزاری کمکی ارائه میدهند که اصلاح شماره 2 را برای شما انجام میدهد. kitty دارای یک ssh kitten است: kitten ssh myserver متصل میشود و terminfo مربوط به kitty را بهطور خودکار به میزبان راه دور کپی میکند. مستندات آن همچنین روش دستی infocmp -a xterm-kitty | ssh myserver tic -x -o \~/.terminfo /dev/stdin را ارائه میدهد که در آن کاراکتر tilde به دلیلی که در بالا ذکر شد، escape شده است. Ghostty دستور infocmp -x xterm-ghostty | ssh YOUR-SERVER -- tic -x - را مستند کرده و یکپارچهسازی با shell (ssh-env و ssh-terminfo) به همراه یک action به نام +ssh ارائه میدهد که هر دو اصلاح را خودکار میکنند.
این ابزارهای کمکی همان دستوراتی را اجرا میکنند که شما بهصورت دستی انجام دادید. دانستن روش دستی همچنان اهمیت دارد، زیرا اگر اتصال از جای دیگری، روی یک jump host یا داخل یک اسکریپت آغاز شود، ابزار کمکی نمیتواند عمل کند.
هنگامی که خطا بازمیگردد
تحت sudo -i. یک shell ورود به سیستم با دسترسی root دارای HOME=/root است و sudo، TERM شما را حفظ میکند. اکنون ncurses مسیر /root/.terminfo را جستجو میکند که ورودی شما را در خود ندارد، بنابراین خطا فقط برای کاربر root بازمیگردد. با استفاده از sudo -i env تأیید کنید و خطوط HOME و TERM را بخوانید. ورودی را در دایرکتوری سیستم نصب کنید یا یک کپی از آن را در /root/.terminfo قرار دهید.
داخل tmux یا screen. این برنامهها TERM خاص خود را برای برنامههایی که درون آنها اجرا میشوند تنظیم میکنند که معمولاً screen-256color یا tmux-256color است. یک سرور قدیمی، screen-256color را میشناسد و ممکن است tmux-256color را نشناسد، بنابراین shell بیرونی سالم است در حالی که همه چیز در داخل session با خطا مواجه میشود. علت همان است، اما نام متفاوت است. دستور tput -T tmux-256color longname را روی سرور اجرا کنید تا ببینید کدامیک از این دو را دارد. یک میز کار ترمینال که حول tmux ساخته شده این موضوع را تا روزی که به میزبانی که هنوز اصلاح نکردهاید متصل شوید، پنهان نگه میدارد.
روی یک سرور تازه. ~/.terminfo برای هر حساب کاربری و هر ماشین منحصربهفرد است، بنابراین یک VPS بازسازیشده دوباره خالی شروع میشود.
صفحه نمایش ترسیم میشود، کلیدها بد عمل میکنند. اگر نمایشگر مشکلی ندارد اما کلیدهای Home، End یا کلیدهای تابعی (function keys) کاراکترهای اضافی درج میکنند، نام به ورودیای ترجمه شده است که با ترمینال شما مطابقت ندارد. توالی کلیدها از همان ورودی terminfo میآیند که قابلیتهای ترسیم از آن میآیند، بنابراین یک ورودی تقریبی، کلیدهای تقریبی میدهد. این نتیجه معمولِ اصلاح 1 روی شبیهسازی است که توالی کلیدهای آن با xterm متفاوت است. اصلاح 2 پاسخ این مشکل است، زیرا فقط ورودی واقعی شبیهساز شما، توالیهای صحیح را حمل میکند.
FAQ
چرا در SSH فقط خطای "unknown terminal type" را میبینم؟
زیرا پایگاه دادهای که باید مشخصات ترمینال شما را در خود نگه دارد، روی سرور قرار دارد. شبیهساز ترمینال شما، توصیفات خود را روی ماشینی که روی آن اجرا میشود نصب میکند، بنابراین برنامههای محلی بدون هیچ تلاشی از سوی شما آن را پیدا میکنند. SSH فقط مقدار TERM را ارسال میکند، زیرا این پروتکل نام را همراه با درخواست pseudo-terminal منتقل میکند. کتابخانه ncurses در سمت سرور، نام را در پایگاه داده راه دور جستجو میکند و چیزی نمییابد؛ در نتیجه initscr با شکست مواجه شده و برنامه متوقف میشود.
آیا میتوانم این مشکل را بدون دسترسی root روی سرور حل کنم؟
بله. کتابخانه ncurses پیش از هر دایرکتوری سیستمی، مسیر $HOME/.terminfo را جستجو میکند، بنابراین وجود یک ورودی در آنجا برای حساب کاربری شما کافی است. دستور infocmp -x | ssh user@host -- tic -x - را یکبار برای هر سرور اجرا کنید. در صورتی که امکان نوشتن در مسیر سیستمی وجود نداشته باشد (که حالت عادی برای یک حساب کاربری بدون امتیاز است)، tic در دایرکتوری home شما مینویسد. وارد سیستم شوید و tput longname را اجرا کنید تا تأیید شود که جستجو اکنون به درستی انجام میشود.
با تنظیم TERM=xterm-256color برای یک میزبان، چه چیزی را از دست میدهم؟
تمام قابلیتهایی که شبیهساز شما فراتر از ورودی xterm-256color توصیف میکند؛ مانند رنگهای 24-بیتی از طریق RGB و خطکشیدنهای استایلدار از طریق Smulx. برنامهها نمیتوانند این قابلیتها را ببینند، بنابراین از نزدیکترین جایگزین (fallback) استفاده میکنند. توالی کلیدهای تابعی (Function-key) نیز ممکن است با ترمینال واقعی شما متفاوت باشد. این تنظیم را در فایل ~/.ssh/config در سمت کلاینت قرار دهید، نه در .bashrc روی سرور، زیرا این کار باعث تنزل کیفیت نشستها برای ترمینالهایی میشود که پیش از آن به درستی کار میکردند.
چرا پس از sudo -i خطا دوباره ظاهر میشود؟
دستور sudo -i یک login shell را با دسترسی root و با استفاده از HOME=/root شروع میکند، در حالی که مقدار TERM شما را حفظ میکند. در این حالت، ncurses به جای دایرکتوری home شما، مسیر /root/.terminfo را جستجو میکند و چیزی نمییابد؛ بنابراین root همان خطایی را میبیند که حساب کاربری شما دیگر با آن مواجه نیست. دستور sudo -i env را اجرا کنید و خطوط HOME و TERM را بررسی کنید. ورودی را با استفاده از sudo tic -x -o /usr/share/terminfo در دایرکتوری سیستمی کامپایل کنید یا آن را در /root/.terminfo کپی کنید.
آیا apt install ncurses-term همیشه مشکل را حل میکند؟
خیر. این دستور فقط ورودیهایی را اضافه میکند که در نسخه ncurses بستهبندیشده توسط توزیع شما وجود داشته است. نسخه Ubuntu 24.04 شامل ncurses 6.4 است (ncurses-bin 6.4+20240113-1ubuntu2.1، تا اوت 2026)، و ورودی Ghostty در نسخه ncurses 6.5-20241228 به بالادست (upstream) اضافه شده است، بنابراین آن بسته نمیتواند شامل این ورودی باشد. پیش از تکیه بر بسته، با tput -T <name> longname بررسی کنید و اگر همچنان ترمینال ناشناخته گزارش شد، خودتان ورودی را منتقل کنید.