Chet Ramey نگارش 4 پوسته Bash را در بیستم فوریه 2009 انتشار داد. این نگارش دارای یک تعداد ویژگی جدید قابل توجه، و همچنین اصلاح باگهای مهم میباشد.
از جمله موارد خواستنی و جالبِ:
آرایههای انجمنی. [1]
مثال 37-5. یک بانک اطلاعات ساده آدرس
#!/bin/bash #fetch_address.sh declare -A address #گزینه -A آرایه انجمنی تعریف میکند. address[Charles]="414 W. 10th Ave., Baltimore, MD 21236" address[John]="202 E. 3rd St., New York, NY 10009" address[Wilma]="1854 Vermont Ave, Los Angeles, CA 90023" echo "Charles's address is ${address[Charles]}." # echo "Wilma's address is ${address[Wilma]}." # echo "John's address is ${address[John]}." # echo echo "${!address[*]}" #شاخصهای آرایه ... #
مثال 37-6. یک بانک اطلاعات آدرس تا اندازهای پیچیدهتر
#!/bin/bash # #یک نگارش پیچیدهتر اسکریپت fetch_address.sh SUCCESS=0 E_DB=99 #کد خطا برای فقدان مدخل. declare -A address #گزینه -A آرایه انجمنی تعریف میکند. store_address () { address[$1]="$2" return $? } fetch_address () { if [[ -z "${address[$1]}" ]] then echo "$1's address is not in database." return $E_DB fi echo "$1's address is ${address[$1]}." return $? } store_address "Lucas Fayne" "414 W. 13th Ave., Baltimore, MD 21236" store_address "Arvid Boyce" "202 E. 3rd St., New York, NY 10009" store_address "Velma Winston" "1854 Vermont Ave, Los Angeles, CA 90023" #تمرین: #فراخوانهای store_address فوق را برای خواندن از یک فایل، سپس تخصیص #+فیلد 1 به نام، فیلد 2 به آدرس در آرایه، بازنویسی کنید. #هر سطر از فایل قالبی نظیر بالا خواهد داشت. از یک حلقه while-read جهت #خواندن از فایل، و از sed یا awk برای تجزیه فیلدها استفاده کنید. fetch_address "Lucas Fayne" # fetch_address "Velma Winston" # fetch_address "Arvid Boyce" # fetch_address "Bozo Bozeman" # exit $? #در این حالت، کد خروج 99 است، چون برگشتی از تابع است.
برای یک کاربرد جالب آرایه انجمنی، مثال A-53 را ببینید.
شاخص عناصر آرایه میتواند شامل کاراکترهای فاصله درج شده باشد، یا حتی کاراکترهای فاصله مقدم و/یا دنباله داشته باشد. با این وجود، شاخص عناصر آرایه شامل فقط فضای سفید مجاز نیست. address[ ]="Blank" # |
موارد تکمیلی برای ساختارcase: خاتمهدهندههای ;;& و ;&.
مثال 37-7. تست کردن کاراکترها
#!/bin/bash test_char () { case "$1" in [[:print:]] ) echo "$1 is a printable character.";;& #پایان دهنده ;;& به معنی ادامه با تست الگوی بعدی است. [[:alnum:]] ) echo "$1 is an alpha/numeric character.";;& [[:alpha:]] ) echo "$1 is an alphabetic character.";;& [[:lower:]] ) echo "$1 is a lowercase alphabetic character.";;& [[:digit:]] ) echo "$1 is an numeric character.";& #خاتمهدهنده ;& جمله بعدی را اجرا میکند... %%%@@@@@ ) echo "********************************";; #...حتی با یک الگوی تقلبی. esac } echo test_char 3 # # # # echo test_char m # # # # echo test_char / # echo #خاتمهدهنده ;;& میتواند شرطهای پیچیده if/then را صرفهجویی کند. #پایاندهنده ;& کمتر مورد استفاده است.
فرمان داخلی جدید coproc دو پردازش موازی برای ارتباط گرفتن و اثر متقابل برهم فراهم میکند. به طوری که Chet Ramey در Bash FAQ [2] نگارش 4.01 اظهار میکند:
یک کلمه رزرو شده جدید coproc وجود دارد که یک کمک پردازش را مشخص میکند:
اجرای یک فرمان به طور غیر همزمان با دو لوله متصل شده به پوسته تولید کننده.
کمک پردازشها میتوانند نامبرده شوند. توصیفگرهای فایل ورودی و خروجی
و PID کمک پردازش در متغیرهایی با نامهای مختص کمک پردازش
برای پوسته فراخوانی کننده در دسترس هستند.
George Dimitriu شرح میدهد
«...coproc ...یک ویژگی مورد استفاده در جایگزینی پردازش Bash است،
که اکنون در دسترس عموم قرار داده میشود.»
این به معنای آن است که اکنون به جای فقط یک مکانیسم پشت پرده مورد استفاده به وسیله Bash،
میتواند به طور صریح در یک اسکریپت فراخوانی بشود.
کمک پردازشها از توصیفگرهای فایل استفاده میکنند. توصیفگرهای فایل وسیله ارتباط برقرار کردن پردازشها و لولهها را فراهم میکنند.
#!/bin/bash #یک کمک پردازش با یک حلقه while-read ارتباط برقرار میکند. coproc { cat mx_data.txt; sleep 2; } # #اجرای این را بدون sleep 2 امتحان کنید و ببینید چه رخ میدهد. while read -u ${COPROC[0]} line #${COPROC[0]} توصیفگر فایل do #+برای کمک پردازش است. echo "$line" | sed -e 's/line/NOT-ORIGINAL-TEXT/' done kill $COPROC_PID #دیگر کمک پردازش نیاز نیست، #+بنابراین kill کردن PID آن.
اما، مراقب باشید!
#!/bin/bash echo; echo a=aaa b=bbb c=ccc coproc echo "one two three" while read -u ${COPROC[0]} a b c; #توجه نمایید که این حلقه یک do #+پوسته فرعی اجرا میکند. echo "Inside while-read loop: "; echo "a = $a"; echo "b = $b"; echo "c = $c" echo "coproc file descriptor: ${COPROC[0]}" done # # # #تا اینجا، خوب است، اما ... echo "-----------------" echo "Outside while-read loop: " echo "a = $a" # echo "b = $b" # echo "c = $c" # echo "coproc file descriptor: ${COPROC[0]}" echo # ---------------------------------------------------------------------------# مترجم: اگر چندان هم خوب نیست، و توصیفگر فایل در آخرین دستور echo بالا نمایش # داده نمیشود، ممکن است لازم باشد سطر فرمان coproc (سطر ۸) را مانند مثال قبل # به صورت coproc { echo "one two three"; sleep 2; } بنویسید. امتحان کنید. # --------------------------------------------------------------------------# کمک پردازش هنوز در حال اجرا است، اما ... #+بازهم پردازش والد قادر به ارث بردن متغیرها #+از پردازش فرزند یعنی حلقه while-read نیست. #این را با اسکریپت badread.sh مقایسه کنید.
کمک پردازش غیر همزمان است، و این ممکن است باعث مشکل بشود. ممکن است قبل از اینکه پردازش دیگر مخابره با آن را تمام کرده باشد، به پایان برسد. #!/bin/bash coproc cpname { for i in {0..10}; do echo "index = $i"; done; } # |
فرمان داخلی جدید mapfile بدون استفاده از یک حلقه یا جایگزینی فرمان، امکان بار کردن یک آرایه با محتویات یک فایل متنی را فراهم میکند.
#!/bin/bash mapfile Arr1 < $0 #مانند همان نتیجه Arr1=( $(cat $0) ) echo "${Arr1[@]}" #تمام این اسکریپت را در stdout کپی میکند. echo "--"; echo #اما، همانند read -a نیست!!! read -a Arr2 < $0 echo "${Arr2[@]}" #فقط سطر اول اسکریپت را داخل آرایه میخواند. exit
فرمان داخلی read کمی بهتر شده است. اکنون گزینه -t برای timeout مقادیر کسری (decimal) را قبول میکند [3] و گزینه -i بارگیری اولیه بافر ویرایش را اجازه میدهد. [4] متاسفانه، این افزایشها هنوز یک کار در جریان پیشرفت هستند و (هنوز) قابل استفاده در اسکریپتها نیستند.
جایگزینی پارامتر عملگرهای تبدیل حالت حروف دریافت میکند.
#!/bin/bash var=veryMixedUpVariable echo ${var} # echo ${var^} # #کاراکتر اول به حرف بزرگ تبدیل گردیده است. echo ${var^^} #VERYMIXEDUPVARIABLE #تمام کاراکترها به حالت بزرگ تبدیل شدهاند. echo ${var,} #veryMixedUpVariable #کاراکتر اول به حرف کوچک تبدیل شده است. echo ${var,,} #verymixedupvariable #تمام کاراکترها به حروف کوچک تبدیل شدهاند.
فرمان داخلی declare اکنون گزینه -l (lowercase) حالت کوچک حروف و -c (capitalize) حروف بزرگ را میپذیرد.
#!/bin/bash declare -l var1 #به حروف کوچک تبدیل خواهد نمود var1=MixedCaseVARIABLE echo "$var1" #mixedcasevariable #همانند اثر echo $var1 | tr A-Z a-z است. declare -c var2 #فقط کاراکتر اول را به بزرگ تبدیل میکند. var2=originally_lowercase echo "$var2" # #همانند اثر echo $var2 | tr a-z A-Z نیست.
بسط ابرو دارای گزینههای بیشتری است.
افزایش-کاهش، تعیین شده در عبارت انتهایی داخل ابروها.
#!/bin/bash echo {40..60..2} # #تمام اعداد زوج، بین 40 و 60. echo {60..40..2} # #تمام اعداد زوج، بین 40 و 60، شمارش معکوس. #در واقع، یک کاهش. echo {60..40..-2} #همان خروجی، علامت منها لازم نیست. #اما، در مورد حروف و علایم چطور؟ echo {X..d} # #کاراکتر \ که یک فاصله را escape میکند نمایش داده نمیشود.
صفرهای پُرکننده، تعیین شده در عبارت اول داخل ابروها، هر عبارتی از خروجی را با تعداد لازم صفرها پیشوند میکند
bash$ echo {010..15} 010 011 012 013 014 015 bash$ echo {000..10} 000 001 002 003 004 005 006 007 008 009 010
استخراج رشته فرعی در پارامترهای مکانی اکنون با $0 به عنوان شاخص صفر شروع میشود. (این یک ناهماهنگی در رفتار پارامترهای مکانی را تصحیح میکند.)
#!/bin/bash #show-params.bash #به Bash نگارش 4+ نیاز دارد. #این اسکریپت را با حد اقل یک پارامتر مکانی فراخوانی کنید. E_BADPARAMS=99 if [ -z "$1" ] then echo "Usage $0 param1 ..." exit $E_BADPARAMS fi echo ${@:0}bash3$ show-params.bash one two three one two three bash4$ show-params.bash one two three show-params.bash one two three #
عملگر جدید ** در globbing، انطباق نام فایلها و دایرکتوریها را به طور بازگشتی انجام میدهد.
#!/bin/bash #filelist.bash shopt -s globstar #باید globstar فعال باشد، وگرنه کار نمیکند. #گزینه پوسته globstar در Bash نگارش 4 جدید است. echo "Using *"; echo for filename in * do echo "$filename" done #تنها فایلهای دایرکتوری جاری ($PWD) را لیست میکند. echo; echo "--------------"; echo echo "Using **" for filename in ** do echo "$filename" done #به طور بازگشتی درخت فایل کامل را لیست میکند. exit #خروجی اسکریپت Using * allmyfiles filelist.bash -------------- Using ** allmyfiles allmyfiles/file.index.txt allmyfiles/my_music allmyfiles/my_music/me-singing-60s-folksongs.ogg allmyfiles/my_music/me-singing-opera.ogg allmyfiles/my_music/piano-lesson.1.ogg allmyfiles/my_pictures allmyfiles/my_pictures/at-beach-with-Jade.png allmyfiles/my_pictures/picnic-with-Melissa.png filelist.bash
متغیر داخلی جدید $BASHPID.
یک تابع درونی جدید مدیریت خطا به نام command_not_found_handle وجود دارد.
#!/bin/bash command_not_found_handle () { #پارامترهای تلویحی را قبول میکند. echo "The following command is not valid: \""$1\""" echo "With the following argument(s): \""$2\"" \""$3\""" # } #$1، $2، وغیره به صورت صریح به تابع عبور داده نمیشوند. bad_command arg1 arg2 # #
توضیح ویرایشی آرایههای انجمنی؟ کمک پردازشها؟ برای Bash بهینهای که میشناختیم و دوست داشتیم آخر چه پیش آمد؟ آیا میتواند ابتلا به «ویژگیگرایی» باشد؟ یا حتی ممکن است از روی غبطه خوردن به پوسته Korn باشد؟ تذکر به Chet Ramey: لطفاً فقط ویژگیهای ضروری را در انتشارهای آینده Bash اضافه کنید -- شاید حلقههای for-each و پشتیبانی از آرایههای چند بُعدی [5]. اکثر کاربران Bash ویژگیهای پیچیدهای مانند اشکالزداهای درونی، میانجیهای پرل، و پیوستهای موشکی توانافزا را نیاز نخواهند داشت، استفاده نخواهند نمود، و احتمالاً خیلی سپاسگزار آنها نخواهند بود. |
نگارش 4.1 از Bash، منتشر شده در May سال 2010، اساساً یک به روزرسانی bugfix بود.
فرمان printf اکنون یک گزینه -v برای تنظیم شاخصهای آرایه قبول میکند.
در درون براکتهای دوگانه، عملگرهای مقایسه رشته > و < اکنون با locale(منطقه) همنوایی میکنند. چون تنظیم منطقه میتواند روی ترتیب مرتبسازی عبارتهای رشتهای اثر کند، این دارای آثار جانبی بر روی تستهای مقایسه درون عبارتهای [[ ... ]] است.
فرمان داخلی read اکنون یک گزینه -N (read -N chars) میپذیرد، که باعث خاتمه یافتن read بعد از تعداد chars کاراکتر میگردد.
مثال 37-8. خواندن تعداد N کاراکتر
#!/bin/bash # num_chars=61 read -N $num_chars var < $0 #خواندن 61 کاراکتر نخست اسکریپت! echo "$var" exit ####### خروجی اسکریپت ###### # #!/bin/bash # Requires Bash version -ge 4.1 ... num_chars=61
Here documentهای قرار داده شده در ساختارهای جایگزینی فرمان $( ... ) میتواند با یک ) ساده خاتمه یابد.
مثال 37-9. استفاده از یک here document برای تنظیم یک متغیر
#!/bin/bash # #به Bash نگارش 4.1 یا بالاتر نیاز دارد ... multi_line_var=$( cat <<ENDxxx ------------------------------ This is line 1 of the variable This is line 2 of the variable This is line 3 of the variable ------------------------------ ENDxxx) #به جای آنچه که Bash 4.0 نیاز دارد: #+که رشته پایانبخش سرحد و پرانتز بسته #+خاتمه دهنده در سطرهای جداگانه باشند. # # echo "$multi_line_var" #به هر حال بازهم Bash این هشدار را صادر میکند. #
نگارش 4.2 از Bash، منتشر شده در فوریه 2011، علاوه بر اصلاح باگها شامل یک تعداد ویژگیهای جدید و افزایشها است.
echo -e '\u2630' #کاراکتر نوار افقی سهگانه. #معادل دستور پر پیچ و خمتر پایین است: echo -e "\xE2\x98\xB0" #قابل شناسایی توسط نگارشهای قبلی Bash. echo -e '\u220F' #PI (حرف یونانی و نماد ریاضی) echo -e '\u0416' #حرف "ZHE" (از حروف سیریلیک) echo -e '\u2708' #نماد هواپیما(فونت Dingbat) echo -e '\u2622' #نماد سهپره رادیواکتیویته echo -e "The amplifier circuit requires a 100 \u2126 pull-up resistor." unicode_var='\u2640' echo -e $unicode_var #نماد مونث printf "$unicode_var \n" #نماد مونث، با سطر جدید #و برای موردی تا اندازهای استادانهتر . . . #میتوانیم نمادهای یونیکد را در یک آرایه انجمنی #+ذخیره نموده، سپس آنها را توسط نام بازیابی کنیم. #برای خوانایی بهتر این کد را در یک ترمینال گنوم #+یا ترمینالی با فونت بزرگ و پر رنگ اجرا کنید. declare -A symbol #آرایه انجمنی. symbol[script_E]='\u2130' symbol[script_F]='\u2131' symbol[script_J]='\u2110' symbol[script_M]='\u2133' symbol[Rx]='\u211E' symbol[TEL]='\u2121' symbol[FAX]='\u213B' symbol[care_of]='\u2105' symbol[account]='\u2100' symbol[trademark]='\u2122' echo -ne "${symbol[script_E]} " echo -ne "${symbol[script_F]} " echo -ne "${symbol[script_J]} " echo -ne "${symbol[script_M]} " echo -ne "${symbol[Rx]} " echo -ne "${symbol[TEL]} " echo -ne "${symbol[FAX]} " echo -ne "${symbol[care_of]} " echo -ne "${symbol[account]} " echo -ne "${symbol[trademark]} " echo
مثال بالا از ساختار بسط رشته $' ... ' استفاده میکند. |
هنگامی که گزینه lastpipe پوسته تنظیم باشد، آخرین فرمان لوله در یک پوسته فرعی اجرا نمیگردد.
مثال 37-10. لولهکشی ورودی به یک فرمان read
#!/bin/bash # line='' #مقدار تهی. echo "\$line = "$line"" # echo shopt -s lastpipe #خطا در Bash نگارش کمتر از 4.2 echo "Exit status of attempting to set \"lastpipe\" option is $?" #1 در Bash کمتر از 4.2، وگر نه 0. echo head -1 $0 | read line #لوله کشی سطر اول اسکریپت به read # ^^^^^^^^^در پوستهفرعی نیست!!! echo "\$line = "$line"" #در نگارشهای قدیمیتر Bash #در Bash نگارش 4.2
این گزینه «تعمیرهای» امکانپذیر برای این اسکریپتها ارایه میکند: مثال 34-3 و مثال 15-8
شاخصهای منفی آرایه شمارش معکوس از انتهای یک آرایه را مجاز میکنند.
مثال 37-11. شاخصهای منفی آرایه
#!/bin/bash # #به Bash نگارش 4.2 یا بالاتر احتیاج دارد. array=( zero one two three four five ) #آرایه ۶ عضوی. # # #شاخصهای منفی آرایه اکنون مجاز است. echo ${array[-1]} # echo ${array[-2]} # # echo ${array[-6]} # #شاخصهای منفی آرایه از آخرین عنصر+1 به عقب میشمارند. #اما، نمیتوانید قبل از ابتدای آرایه را فهرست کنید. echo ${array[-7]} # #پس، این ویژگی جدید برای چه کاری خوب است؟ برای: echo "The last element in the array is "${array[-1]}"" #که بدون تردید اندکی قابلفهمتر از این است: echo "The last element in the array is "${array[${#array[*]}-1]}"" echo #و ... index=0 let "neg_element_count = 0 - ${#array[*]}" #تعداد عناصر، به یک عدد منفی تبدیل شده. while [ $index -gt $neg_element_count ]; do ((index--)); echo -n "${array[index]} " done #عناصر آرایه را به طور برعکس لیست میکند. #ما در این آرایه درست فرمان tac را شبیهسازی کردهایم. echo #همچنین neg-offset.sh را ببینید.
استخراج رشته فرعی از یک پارامتر طول منفی برای تعیین نقطه شروع از انتهای رشته هدف، استفاده میکند.
مثال 37-12. پارامتر منفی در ساختار استخراج رشته
#!/bin/bash # #Bash، نگارش 4.2 و بالاتر #طول منفی در استخراج رشته فرعی. #مهم: برداشت از این ساختار را عوض میکند! stringZ=abcABC123ABCabc echo ${stringZ} # # echo ${stringZ:2:3} # #شمردن دوکاراکتر مقدم از ابتدای رشته، و استخراج ۳ کاراکتر. # #تا اینجا چیز جدیدی نیست، اما اکنون ... # # echo ${stringZ:3:-6} #ABC123 # #تعیین شاخص 3 از ابتدا و شاخص 6 از انتهای رشته #+و استخراج رشته فرعی از نشانه اول تا نشانه دوم. # #وقتی پارامتر length منفی باشد، به عنوان پارامتر #+تعیین نقطه انتهایی رشته فرعی به کار میرود. #همچنین neg-array.sh را ببینید.
[1] | به طور دقیقتر، Bash 4+ دارای پشتیبانی محدود برای آرایههای انجمنی است. یک پیادهسازی حداقلی است، و فاقد بسیاری از امکانات چنین آرایههایی در سایر زبانهای برنامهنویسی میباشد. توجه نمایید که، به هر حال، به نظر میرسد آرایههای انجمنی در Bash به طور کارآمدتر و سریعتری نسبت به آرایههای دارای شاخص عددی عمل میکنند. |
[2] | Copyright 1995-2009 by Chester Ramey |
[3] | این فقط با لولهها و برخی فایلهای خاص دیگر کار میکند. |
[4] | اما تنها در پیوستگی با readline، یعنی از خط فرمان. |
[5] | و در حالیکه شما درگیر آن هستید، تعمیر نمودن مشکل read لولهکشی شده انگشتنما را رسیدگی کنید. |