دسته بندی:

تغییر Shell از bash به sh

پاسخ کوتاه: شل ورود هر کاربر لینوکس در آخرین ستون فایل /etc/passwd ثبت شده است و برای تغییر آن از chsh -s /bin/sh username یا usermod -s /bin/sh username استفاده می‌کنید؛ تغییر از جلسه ورود بعدی اعمال می‌شود، نه در همان ترمینال باز. اگر فقط برای یک جلسه کاری به sh نیاز دارید، دستور exec /bin/sh کافی است و هیچ چیزی روی سیستم به‌طور دائم عوض نمی‌شود.

اول ببینید همین حالا در کدام شل هستید

سه دستور زیر سه چیز متفاوت را نشان می‌دهند و خلط کردنشان منشأ بیشتر سردرگمی‌هاست:

  • ps -p $$ -o comm= — شلی که همین لحظه دستورهای شما را اجرا می‌کند. دقیق‌ترین جواب همین است.
  • echo $SHELL — شل ورودی که در /etc/passwd برای شما ثبت شده. اگر با exec /bin/sh موقتاً شل عوض شده باشد، این متغیر همچنان مقدار قبلی را نشان می‌دهد.
  • getent passwd $USER — رکورد کامل کاربر؛ ستون هفتم آن مسیر شل ورود است. برای دیدن فقط همان ستون: getent passwd $USER | cut -d: -f7

دستور قدیمی echo $0 هم کار می‌کند، اما خروجی‌اش در شل ورود به شکل -bash با یک خط تیره ابتدایی است و داخل اسکریپت اصلاً نام شل را نمی‌دهد، بلکه نام خود فایل اسکریپت را برمی‌گرداند. روی یک سرور مجازی ابری ایران که تازه تحویل گرفته‌اید، اولین کاری که ارزش دارد انجام دهید همین است: بدانید کاربرتان با چه شلی وارد می‌شود.

شل‌های مجاز سیستم را بشناسید

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

cat /etc/shells

این فایل صرفاً جنبه اطلاع‌رسانی ندارد. دستور chsh وقتی کاربر عادی آن را اجرا کند، فقط مسیرهای موجود در همین فایل را می‌پذیرد و هر مسیر دیگری را با این توضیح که در /etc/shells فهرست نشده رد می‌کند؛ متن دقیق پیام خطا بین توزیع‌ها و نسخه‌ها فرق دارد، پس روی رشته پیام حساب باز نکنید. همان دستور وقتی با کاربر root اجرا شود این محدودیت را ندارد و صرفاً یک هشدار می‌دهد و تغییر را اعمال می‌کند — که دقیقاً همان جایی است که سرورها خراب می‌شوند.

تغییر موقت در برابر تغییر دائم

اگر هدفتان فقط تست یک اسکریپت زیر sh است، به هیچ وجه شل ورود را دست نزنید. کافی است:

exec /bin/sh

با این دستور فرایند شل فعلی جای خود را به sh می‌دهد و با بستن جلسه همه چیز به حالت اول برمی‌گردد. اگر می‌خواهید بتوانید به bash برگردید، به‌جای exec فقط sh را بزنید؛ آن‌وقت یک شل تازه به‌عنوان زیرفرایند باز می‌شود و با exit به bash قبلی برمی‌گردید.

تغییر دائم: chsh یا usermod

برای تغییر شل خودتان: chsh -s /bin/sh

برای تغییر شل کاربر دیگر، با دسترسی root: chsh -s /bin/sh username یا usermod -s /bin/sh username

یک نکته که در راهنماهای قدیمی اشتباه نوشته شده: chsh رمز عبور خود شما را می‌پرسد، نه رمز root را. اگر همین حالا کاربر root هستید یا با sudo اجرا می‌کنید، اصلاً پرسشی مطرح نمی‌شود. نکته دوم اینکه روی توزیع‌های خانواده RHEL مثل AlmaLinux و Rocky، دستور chsh در نصب‌های کمینه وجود ندارد و باید بسته آن را نصب کنید: dnf install util-linux-user. در محیط‌های کانتینری هم معمولاً usermod در دسترس‌تر است.

بعد از تغییر، حتماً نتیجه را راستی‌آزمایی کنید و سپس یک بار خارج و دوباره وارد شوید؛ ترمینال باز فعلی تا لحظه بسته شدن همان شل قبلی را نگه می‌دارد. روی یک سرور اختصاصی ایران که چند کاربر سرویس روی آن تعریف شده، این راستی‌آزمایی را برای تک‌تک کاربران انجام دهید، نه فقط کاربری که با آن وارد شده‌اید.

bash و sh واقعاً چه فرقی دارند

sh نام یک استاندارد است، نه یک برنامه مشخص. در عمل /bin/sh یک لینک به مفسر دیگری است و اینکه به کدام برنامه اشاره می‌کند، از توزیعی به توزیع دیگر فرق دارد: روی دبیان و اوبونتو معمولاً به dash اشاره می‌کند و روی خانواده RHEL به خود bash که در حالت سازگار با POSIX اجرا می‌شود. با این دستور می‌توانید ببینید روی سرور شما به کجا اشاره دارد:

ls -l /bin/sh

bash علاوه بر امکانات استاندارد، مجموعه‌ای از قابلیت‌های اختصاصی دارد که به آن‌ها bashism می‌گویند. مهم‌ترین‌هایشان:

در bash کار می‌کند در sh استاندارد جایگزین
تطبیق الگو: [[ $a == b* ]] case $a in b*) ... esac
برابری ساده: [[ $a == "$b" ]] [ "$a" = "$b" ]
آرایه‌ها: arr=(x y z) پشتیبانی نمی‌شود؛ از پارامترهای موقعیتی یا رشته جداشده استفاده کنید
source file . file
function name() { name() {
{1..10} دستور بیرونی seq 1 10 یا یک حلقه while
<(command) فایل موقت یا لوله نام‌دار

چرا اسکریپت سالم شما زیر sh می‌شکند

نشانه معمول این است: اسکریپتی که تا دیروز اجرا می‌شد، حالا خطاهایی مثل [[: not found یا Syntax error: "(" unexpected می‌دهد. علتش این است که اسکریپت با sh script.sh اجرا شده یا خط اول آن #!/bin/sh است، در حالی که کدش bashism دارد.

دقت کنید که شل ورود کاربر تعیین‌کننده مفسر اسکریپت نیست. مفسر را خط shebang در سطر اول فایل تعیین می‌کند، و اگر اسکریپت را صریحاً با sh script.sh صدا بزنید، همان shebang هم نادیده گرفته می‌شود. پس اگر کدتان به bash نیاز دارد، خط اول را #!/bin/bash بگذارید و اسکریپت را با ./script.sh اجرا کنید.

برای بازرسی خودکار، ابزار امروزی این کار shellcheck است. اگر اسکریپت را با shellcheck -s sh script.sh بررسی کنید، فایل را با معیار POSIX می‌سنجد و هر bashism را همراه شماره خط و کد خطا گزارش می‌دهد؛ نام بسته آن بسته به توزیع فرق می‌کند، پس با مدیر بسته خود جست‌وجویش کنید. اگر اسکریپت shebang ندارد یا می‌خواهید حالت بررسی را صریح تثبیت کنید، سطر # shellcheck shell=sh را بالای فایل بگذارید. ابزار قدیمی‌تر checkbashisms از بسته devscripts هم همین کار را می‌کند، اما فقط در مخازن دبیان و اوبونتو موجود است و روی خانواده RHEL به آن دسترسی ندارید. توجه داشته باشید که سازگاری کد اسکریپت‌های خودتان همیشه بر عهده خودتان است؛ آنچه در سرور مجازی مدیریت‌شده ایران بر عهده تیم آنلاین سرور قرار می‌گیرد، نصب و پیکربندی کنترل‌پنل و عیب‌یابی نرم‌افزارهای نصب‌شده روی سرور است، نه بازنویسی اسکریپت‌های شما.

هشدار جدی: شل اشتباه یعنی قفل شدن کاربر

اگر مسیری که به -s می‌دهید وجود نداشته باشد یا غلط تایپ شده باشد، ورود آن کاربر شکست می‌خورد. اینجا یک تله مهم هست: برخلاف chsh، دستور usermod اصلاً بررسی نمی‌کند که مسیر داده‌شده وجود دارد یا در /etc/shells فهرست شده است — هر رشته‌ای را بی‌چون‌وچرا در /etc/passwd می‌نویسد. پس یک غلط تایپی ساده در usermod بی‌سروصدا قبول می‌شود و تازه در ورود بعدی خودش را نشان می‌دهد. همچنین مقادیر /sbin/nologin، /usr/sbin/nologin و /bin/false عمداً برای مسدود کردن ورود تعاملی طراحی شده‌اند. اگر این را روی حساب مدیریتی خودتان بنشانید و کلید یا کنسول دیگری نداشته باشید، راه برگشتی از SSH نخواهید داشت.

سه قاعده که این ریسک را حذف می‌کند:

  • پیش از تغییر، وجود فایل شل را تأیید کنید: test -x /bin/sh && echo ok
  • شل root را دست نزنید و روی /bin/bash نگه دارید.
  • یک جلسه SSH باز را نبندید و در یک ترمینال دوم ورود مجدد را آزمایش کنید. اگر شکست خورد، از جلسه اول تغییر را برگردانید: usermod -s /bin/bash username

اگر همه راه‌ها بسته شد، بازیابی از طریق کنسول یا KVM سرور و ویرایش ستون آخر /etc/passwd انجام می‌شود؛ پس قبل از دست زدن به شل، مطمئن شوید به کنسول دسترسی دارید.

روی سرورهای دارای کنترل پنل

روی سرورهایی که کنترل پنل نصب دارند، شل کاربران معمولاً از خود پنل مدیریت می‌شود و تغییر دستی /etc/passwd ممکن است در همگام‌سازی بعدی پنل بازنویسی شود. اگر روی سرورتان cPanel & WHM نصب است — چیزی که با تهیه لایسنس سی‌پنل فعال می‌شود — شل هر حساب از بخش Manage Shell Access در WHM تعیین می‌شود و علاوه بر شل عادی، گزینه شل محدودشده (jailshell) هم وجود دارد که کاربر را در محیط ایزوله نگه می‌دارد. قاعده ساده است: هر جا پنل ابزار خودش را دارد، از همان استفاده کنید.

جمع‌بندی

برای یک تست موقت exec /bin/sh بزنید، برای تغییر دائم chsh یا usermod، و بعد از هر تغییر با getent passwd username نتیجه را بررسی کنید و ورود مجدد را در یک جلسه دوم آزمایش کنید. اگر هدفتان اجرای اسکریپت با یک مفسر خاص است، اصلاً نیازی به تغییر شل ورود ندارید؛ کافی است shebang درست را در سطر اول اسکریپت بگذارید.