POSIX یه شل نیست
خلاصهٔ کاملتر
نویسنده — الکساندر گومز گایگالاس — با یه جملهی آشنا شروع میکنه: «برای قابلحمل بودن، با POSIX shell بنویس.» به گفتهی او این توصیه نیت خوبی داره ولی یه خطای پایهای توش هست: POSIX یه مشخصات فنیه، نه یه برنامه. چیزی که واقعاً اسکریپت رو اجرا میکنه bash، dash، ash، ksh، yash یا یکی از دهها شل دیگهست که هر کدوم استاندارد رو با شکافها، افزونهها و تصادفهای تاریخی خودشون پیاده کردن.
آزمایشی که میآره از این سادهتر نمیشه — یه خط، بدون فلگ و تابع:
#!/bin/sh
echo "C:\new"روی bash و ksh دقیقاً همون چیزی که تایپ کردی برمیگرده، ولی روی dash — که روی دبیان، اوبونتو و آلپاین همون /bin/sh ـه — بکاسلش-n بهعنوان خط جدید تفسیر میشه و خروجی میشکنه. نویسنده میگه وقتی همین خط رو روی مجموعه شلهاش اجرا کرده، تقریباً نصفبهنصف تقسیم شدن.
نکته اینه که استاندارد این دعوا رو حل نمیکنه: POSIX رفتار echo با بکاسلش رو صراحتاً implementation-defined گذاشته و توصیه کرده بهجاش از printf استفاده کنی. پس «echo استاندارد POSIX» اصلاً یه رفتار نیست که بشه اسکریپت رو رویش نوشت — یه اختلافنظرِ مستنده. و این هم یه گوشهی نامتعارف نیست؛ echo اولین دستوریه که هر کسی یاد میگیره.
نویسنده این وضع رو به گویشهای یه زبان تشبیه میکنه: پرتغالی برزیلی و اروپایی هر دو پرتغالیان، ولی یکی نیستن. جامعهی توسعهدهندهها هم بهقول او تظاهر میکنه که اینطور نیست: میگیم «اسکریپت sh» و منظورمون bash ـه، میگیم «POSIXسازگار» و منظورمون «تو CI کار کرد»، میگیم «قابل حمل» و منظورمون «رو دو تا ماشینی که امتحان کردم اجرا شد».
تو پروژهای به اسم shell-docs، هر قابلیت مستندشده رو روی ۱۴ شل اجرا و خروجیها رو مقایسه میکنه. بیشتر چیزها یکسانان: $# همهجا کار میکنه و بسط حسابی هم فراگیره (هرچند تقسیم بر صفر فرق میکنه). ولی local اصلاً تو POSIX نیست و هر شل قواعد scope خودش رو داره، و [[ ]] تو dash وجود نداره — یعنی اگه زیر #!/bin/sh ازش استفاده کنی، روی هر سیستمی که shاش dash ـه بیصدا شکست میخوره.
به گفتهی نویسنده اینا باگ نیستن؛ تصمیمهای انباشتهی دههها پیادهسازی مستقلان که هر کدوم یه نقطهی متفاوت از تاریخ استاندارد (۱۹۸۸، ۲۰۰۱، ۲۰۱۷) رو هدف گرفتن. شاهدش هم اینه که /bin/sh امروز روی اوبونتو ۲۴.۰۴ همون dash 0.5.12 ـه، روی مکاواس هنوز bash 3.2.57 (پونزده ساله دستنخورده، بهخاطر لایسنس GPLv3)، روی آلپاین ash بیزیباکس و روی بعضی توزیعهای سازمانی ksh93.
حرف آخر مقاله سادهست: اگه #!/bin/bash مینویسی و منظورت هم واقعاً همونه، اشکالی نداره — دستکم صادقانهست. ولی تا #!/bin/sh مینویسی، داری قولی میدی که باید راستیآزماییش کنی. «قابل حمل» یعنی تستشده روی همون بازهای از شلها که واقعاً روشون اجرا میشه، و این کاریـه که خودبهخود انجام نمیشه.
نکات کلیدی:
- POSIX یه مشخصاته، نه یه شل؛ مجری اسکریپت شما bash یا dash یا ash ـه، نه «POSIX»
- رفتار echo با بکاسلش تو استاندارد implementation-defined ـه؛ توصیهی خود POSIX استفاده از printf ـه
- local تو POSIX نیست و [[ ]] تو dash وجود نداره — زیر #!/bin/sh بیصدا میشکنه
- روی اوبونتو /bin/sh همون dash ـه، روی مک bash 3.2 و روی آلپاین ash بیزیباکس
- «قابل حمل» یعنی تستشده روی شلهای هدف، نه «تو CI سبز شد»




