bashism - آموزش اسکریپت نویسی
X
تبلیغات
رایتل

آموزش اسکریپت نویسی

آموزش اسکریپت نویسی پوسته گنو-لینوکس

#!/bin/bash

bashism

Bashism

چگونه اسکریپت‌های bash بسازیم که در dash کار کنند

این صفحه کوششی است برای لیست کردن برخی از رایج‌ترین bashismها، یعنی ویژگی‌هایی که توسط POSIX تعریف نشده‌اند (در dash، یا ‎/bin/sh‎ متداول، کار نخواهند کرد). احتمالاً شامل تمام جزئیات نخواهد بود. همچنین توجه نمایید که ما در باره "bashism" صحبت می‌کنیم، زیرا این ویکی به طور عمده متمرکز بر bash می‌باشد، اما چون اکثر ویژگی‌های اسکریپت‌نویسی Bash از ksh مشتق گردیده است تعدادی(تقریباً همه) از این ملحقات احتمالاً با تفاوت‌هایی در جزئیات، حداقل در برخی پوسته‌های دیگر مانند ksh یا zsh نیز کار می‌کنند. POSIX تعداد بسیار کمتری از آنها را لازم دانسته است.

ترکیب

در bash کار می‌کند

تغییر برای dash

توضیحات

تعریف توابع

function f { echo hello world; }

f() { echo hello world; }

"function" توسط POSIX تعریف نگردیده است، فقط شکل ‎"name ()"‎ وجود دارد. ترکیب ‎function f {...}‎ در ksh آغاز گردید (پیش‌تر از ترکیب دستوری Bourne). در ksh هر دو کل وجود دارد، اما در پیاده‌سازی‌های ‎AT&T‎ توابع تعریف شده با ‎"function"‎ به طور مختصری متفاوت کار می‌کنند. zsh نیز بدون تمایز هر دو ترکیب را پشتیبانی می‌کند.

در case

;;& ;& etc

هیچ. case را دوگانه کنید( برای اجتناب از دوبار تکرار کد، از تابع استفاده کنید)

;;&‎ و ‎;&‎ در bash4 هست و در POSIX تعریف نشده. ksh پیاده‌سازی ‎AT&T‎ (از ksh88e، جایی که آغاز گردید)، ‎MirBSD ksh‎ (از R40) و zsh (از ‎3.1.2‎) دارای ‎;&‎ هستند، اما ‎;;&‎ را ندارند

حلقه forعددی سَبک C

for ((i=0; i<3; i++)); do
 echo "$i"
done

i=0 ; while [ "$i" -lt 3 ]; do
 echo "$i" ; i=$(($i+1))
done

این ترکیب توسط POSIX تعریف نشده. در ksh93 جایی که سرچشمه آن است و در zsh وجود دارد.

بسط رشته‌ها

echo $'hello\tworld'

printf "hello\tworld\n"

به طور تاریخی ‎$' '‎ تا سال 2008 توسط POSIX تعریف نشده بود، اما در نگارش بعدی پذیرفته شده است. http://austingroupbugs.net/view.php?id=249. در ksh93 آغاز شد و توسط zsh نیز پشتیبانی شد.

glob توسعه‌یافته

+( ) @( ) !( ) *( )

همیشه امکان پذیر نیست، گاهی می‌توانید از چند globs استفاده کنید، گاهی اوقات می‌توانید ‎find(1)‎ را به کار ببرید

توسط POSIX تعریف نشده. در ksh آغاز گردید. به وسیله zsh با یک گزینه مانند bash پشتیبانی شده است.

select

select

برخی نظرات: خودتان منو را پیاده‌سازی کنید، از برنامه‌ای مانند dialog استفاده کنید

در POSIX تعریف نشده. در ksh شروع شد، در zsh نیز حاضر است.

بسط‌ها

  • بسط ابرو، به عنوان مثال ‎{a,b,c}‎ یا ‎{1..10}‎ در POSIX تعریف نشده است. هر دو شکل در ksh93، و شکل اول در ksh قدیمی‌تر و mksh وجود دارد. . اولی در csh آغاز گردید، دومی در zsh.

  • جایگزینی پردازش ‎<( )‎ و ‎>( )‎ توسط POSIX تعریف نشده، اما می‌تواند با FIFOها شبیه‌سازی شود: به جای ‎foo <(bar)‎، بنویسید ‎mkfifo fifo; bar > fifo & foo fifo‎ (در اصل این طریقه‌ایست که جایگزینی پردازش در سیستم عامل‌هایی که مکانیسمی مانند ‎/dev/fd/‎ را ندارند، برای ارجاع به لوله‌های بدون نام با نام فایلها، پیاده‌سازی می‌شود). در ksh93 شروع گردید، در zsh نیز وجود دارد.

بسط‌های پارامتر

فهرست بسط‌های تعریف نشده در POSIX:

  • ${name:n:l}‎ -- می‌توانید از این ‎$(expr "x$name" : "x.\{,$n\}\(.\{,$l\}\)")‎ استفاده کنید. این بسط در ksh93 شروع شد و در zsh نیز موجود است.

  • ${name/foo/bar}‎ -- بعد از تغییر الگوهای پوسته به عبارت‌های منظم می‌توانید از این سطرِ فرمان ‎$(printf '%s\n' "$name" | sed 's/foo/bar/')‎ استفاده کنید. این ترکیب در ksh93 آغاز شد و در mksh، و zsh، موجود است، اما بسط جایگزینی ksh93 با قالب Bash تفاوت دارد.

  • ${!name}‎ -- مختص bash، برای رسیدن به نتایج مشابه استفاده از eval امکان پذیر است، اما مستلزم توجه بسیار به جزئیات خواهد بود، پرسش و پاسخ شماره ۶ را ببینید.

  • رفتار عملگرهای #، ##، %، و %% موقع استفاده همراه پارامترهای @ یا * در مستندات POSIX و ksh88 مشخص نگردیده است. Dash حذف حواشی برای نتیجه پاکیزه شده را فراهم می‌کند. mksh و pdksh با آن به عنوان یک بسط نامعتبر رفتار می‌کنند.

توجه کنید که استفاده از ‎$( )‎ دارای اثرات جانبی حذف سطر جدید انتهایی از نتایج است.

آرایه‌ها

آرایه‌ها در POSIX تعریف نشده‌اند( اما در ksh موجودند)، چاره‌جویی کلی آسانی برای آرایه‌ها وجود ندارد. در اینجا اشاراتی شده است::

  • پارامترهای مکانی یک نوع آرایه ‌هستند( تنها یک آرایه ):

# یک فرمان را پویا می‌سازد( پرسش و پاسخ شماره ۵۰ را ببینید )‏‎
set -- 'mycommand' 'needs some complex' 'args'
"$@"
# i‎ دستیابی پارامتر شماره 
set -- one two three
i=2
eval "var=\${$i}" #  .باید در تمام موارد توسط اسکریپت کنترل بشود. اگر از آثار جانبی همچون ورودی کاربر، تاثیر بپذیرد، معتبر سازی نیرومندی لازم می‌گردد i 
printf '%s\n' "$var"
  • استفاده از IFS و ‎set -f

  • eval قدرتمند، اما آسان برای سوءاستفاده در روشهای پُرمخاطره است. فرمان Eval و مسائل امنیت را ببینید.

شرطی‌ها

در bash کار می‌کند

تغییر برای کار کردن در dash

توضیحات

test ساده

[[

از ‎[ و نقل‌قولهای دوگانه در اطراف بسط‌ها استفاده کنید ‎[ "$var" = "" ]

‎[[‎ توسط POSIX تعریف نشده، در ksh شروع شوه و در zsh نیز وجود دارد

انطباق الگو

[[ foo = *glov ]]

از case یا expr یا grep استفاده کنید

پرسش و پاسخ شماره ۴۱ را ببینید

بررسی برابری

==

به جای آن از = استفاده کنید

در POSIX فقط = تعریف گردیده

مقایسه لغوی به ترتیب دیکشنری.

< >

بدون تغییر

در ‎dash, ksh, yash‎ و zsh موجود است، اما در POSIX تعریف نگردیده است. برای روشهای چاره‌جویی ممکن یادداشت پایین را ببینید.

مقایسه زمانهای ویرایش

[[ file1 -nt file2 ]] یا -ot

[ "$(find 'file1' -prune -newer 'file2')" ] یا [ "file1" -nt "file2" ]

گزینه ‎-prune‎ برای پرهیز از جستجوی بازگشتی لازم است، در‏ dash، ksh، yash و zsh وجود دارد. ‎-nt‎ و ‎-ot‎ توسط POSIX تعریف نگردیده‌اند.

بررسی آنکه آیا دو فایل hardlink هستند

[[ file1 -ef file2 ]]

[ "file1" -ef "file2" ]

-ef‎ در POSIX تعریف نگردیده، اما در ‏ksh، yash، zsh و Dash وجود دارد.

(( ))

(( ))‎ (بدون $) مانند یک فرمان بر خودش عمل می‌کند

برای مقایسه ساده: ‎[ -lt ] (and -ne -gt -ge)‎ یا ‎[ "$((3 + 1 < 5))" -ne 0 ]‎.

برای تخصیص یک متغیر ‎var=$((3+1))‎

در ksh (جایی که از آن آغاز شده) و zsh وجود دارد

یادداشت: برای مقایسه‌های لغوی چند برنامه استاندارد POSIX می‌توانند به کار بروند. مثالهای زیر در صورتیکه محتوای ‎$a‎ قبل از ‎$b‎ مرتب گردد، یک وضعیت خروج صحیح (صفر) برگشت می‌دهند.

  • awk 'BEGIN { exit !(ARGV[1] "" < "" ARGV[2]) }' "$a" "$b"

  • expr "x$a" "<" "x$b" >/dev/null

  • اگر متغیرها شامل کاراکترهای سطر جدید نباشند: ‎printf "%s\n" "x$a" "x$b" | sort -C‎ ( در صورتیکه ‎$a‎ و ‎$b‎ برابر باشند نیز صحیح برگشت می‌دهد)

حساب

در bash کار می‌کند

تغییر برای dash

توضیحات

پیش/پس افزایش/کاهش

++ --

i=$((i+1))‎ یا ‎: $((i+=1))

-

عملگر کاما

,

: "$((...))"; cmd "$((...))"

عملگر کاما به طور گسترده تقریباً توسط همه به استثنای dash و yash پشتیبانی شده است -- حتی توسط posh و Busybox.

-

let یا ((...))

[ "$((...))" -ne 0 ]

به علت محدودیت کاما در بالا، let نمی‌تواند بدون یک حلقه به طور دقیق شبیه‌سازی گردد.

تغییر مسیرها

در bash کار می‌کند

تغییر برای dash

توضیحات

تغییر مسیر stdout و stderr با هم

>& and &>

command > file 2>&1 or command 2>&1 | othercommand

-

|& (bash4)

command 2>&1 | othercommand

با ksh ناسازگار است. حتی در Bash توصیه نمی‌شود. فقط ‎2>&1‎ را به کار ببرید.

دونسخه کردن و بستن

m>&n- m<&n-

m>&n n>&-

توسط POSIX تعریف نگردیده

herestring

<<<"string"

echo | command‎، یا یک ‎here document‎ به منظور پرهیز از یک پوسته فرعی ‎(<<EOF)‎

-

داخلی‌ها

  • echo -n‎ یا ‎-e‎ -- استاندارد POSIX هیچ گزینه‌ای تعریف نکرده، و از این گذشته اجازه می‌دهد ‎echo -e‎ رفتار پیش‌فرض باشد. به جای آن ‎printf "%s\n"‎ (برای echo معمولی) یا ‎printf "%b\n"‎ (برای ‎echo -e‎) را به کار ببرید، از ‎\n‎ به منظور شبیه‌سازِی ‎echo -n‎ دست بکشید.

  • printf -v‎ توسط POSIX تعریف نمی‌شود، و فقط Bash آن را پشتیبانی می‌کند. قالب‌های ‎%q‎ و ‎%()T‎ توسط POSIX تعریف نمی‌شوند، اما به وسیله ksh93 و Bash پشتیبانی می‌گردند. قالب‌های a، ‏A، ‏e، ‏E، ‏f، ‏F، ‏g، و G در POSIX برای ‎printf(1)‎ عمل نمی‌کنند اما dash ظاهراً ‎%f‎‏، ‎%e‎‏، ‎%E‎‏، ‎%g‎، و ‎%G‎ را پشتیبانی می‌کند.

  • read -- تنها گزینه تعریف شده در POSIX گزینه ‎-r‎ است، ksh یک مجموعه از گزینه‌های مختلف دارد که فقط به طور جزئی با bash همپوشانی دارد.

  • shopt، و بنابراین تمام گزینه‌هایی که فراهم می‌کند (extglob و nullglob و dotglob وغیره) توسط POSIX تعریف نشده‌اند و مخصوص bash می‌باشند.

  • local -- معادلی در POSIX وجود ندارد. می‌توانید از ‎$funcname_varname‎ برای کاهش احتمال تضاد، استفاده کنید، اما حتی آن نیز برای توابع بازگشتی کافی نیست . می‌توانید تضمین کنید که فراخوانی‌های بازگشتی در محیط پوسته‌های فرعی رخ می‌دهد (همینطور یک کپی "local" از تمام متغیرها وجود دارد)، یا تمام متغیرهای محلی را به عنوان پارامتر عبور بدهید( زیرا پارامترهای مکانی ‎$@‎، و ‎$1‎، و ‎$2‎، و غیره به درستی local هستند ). dash به طور صریح از local به عنوان یک الحاقیه غیر Posix استفاده می‌کند، در عوض ksh از typeset استفاده می‌نماید، که مانند declare پوسته bash کار می‌کند. local به وسیله LSB و معیارهای سیاست Debian حکم گردیده، اگرچه فقط ترکیب دستوری ‎local varname‎ (نه ‎local var=value‎) مشخص شده است. یک پیاده‌سازی از یک متغیر پشته برای پوسته‌های POSIX را در اینجا می‌توان یافت.

متغیرهای خاص

در bash کار می‌کند

تغییر برای dash

توضیحات

نگهداری یک رکورد از زمانها

SECONDS

before=$(date +%s) ....seconds=$(( $(date +%s) - $before))

date +%s‎ موافق POSIX نیست،برای اطلاعات بیشتراین پرسش و پاسخ را ببینید. در ksh وجود دارد

تولید یک عدد تصادفی

RANDOM

random=$(awk 'BEGIN{srand(); printf "%d\n",(rand()*256)}')‎ یک عدد بین 0 و 256 می‌دهد.
random=$(hexdump -n 1 -e '/1 "%u"' /dev/urandom)‎ و ‎random=$(od -A n -N 1 -t u1 /dev/urandom)‎ یک عدد مستقل از زمان، بین 0 و 256 می‌دهند.
random=$(hexdump -n 2 -e '/2 "%u"' /dev/urandom)‎ و ‎random=$(od -A n -N 2 -t u2 /dev/urandom)‎ یک عدد مستقل از زمان، بین 0 و 65535 می‌دهند.

اطمینان حاصل نمایید که آموخته‌اید ‎srand()‎ و ‎rand()‎ چه کار می‌کنند، یعنی در صورتیکه چندین مرتبه به سرعت awk را فراخوانی کنید این روش شکست می‌خورد. در عوض تمام اعدادی را که لازم دارید، داخل awk تولید کنید. برخی سیستم‌هاهمچنین ‎/dev/random and /dev/urandom‎ را هم فراهم می‌کنند، اما این مورد ضرورتاً توسط استاندارد POSIX حکم نمی‌شود. ksh دارای RANDOM هست.

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

PIPESTATUS

راه حل ساده:
mkfifo fifo; command2 <fifo & command1 >fifo; echo "$?"
لوله‌های با نام را ببینید

مخصوص bash، این پرسش و پاسخ و این اسکریپت وضعیت لوله برای پوسته POSIX را ببینید

به دست آوردن نام تمام(های) توابع یا تابع جاری

FUNCNAME

??

مختص bash، پرسش stackoverflow را ببینید

بیشتر

  • مستندات bash دارای لیستی از تفاوتهای میان bash در حال اجرا در وضعیت POSIX و یک bash معمولی، است.

به خاطر داشته باشید که bash در وضعیت POSIX فقط متعهد به اجرای یک پوسته نوشته‌شده مطابق معیارهای POSIX است. این به معنای آن نیست که اگر شما در اسکریپت‌هایتان از bashismها استفاده کنید، ناموفق خواهد شد.

  • یک اسکریپت پرل سودمند checkbashisms وجود دارد که بخشی از بسته devscripts دبیان است که می‌تواند به نمایان کردن bashismها در یک اسکریپت خاص کمک کند.
  • ویکی Ubuntu نیز دارای صفحه‌ای است که تفاوتها را شرح می‌دهد.


CategoryShell

Bashism (آخرین ویرایش ‎2013-10-24 13:06:44‎ توسط GreyCat)