اجرای یک اسکریپت پوسته، پردازش جدیدی راهاندازی میکند، یک پوسته فرعی.
تعریف: پوسته فرعی، یک پردازش فرزند راهاندازی شده به وسیله یک پوسته (یا اسکریپت پوسته) است. |
پوسته فرعی یک نمونه جداگانه از پردازشگر فرمان -- پوستهای که در کنسول یا در پنجره xterm یک اعلان به شما میدهد -- است. درست همانطور که فرمانهای شما در اعلان خط فرمان تفسیر میشوند، به طور مشابهی یک اسکریپت پردازش دستهای لیستی از فرمانها را انجام میدهد. هر اسکریپت پوسته در حال اجرا، در عمل یک پردازش فرعی (پردازش فرزند) از پوسته پدر است.
یک اسکریپت پوسته خودش میتواند پردازشهای فرعی راهاندازی نماید. این پوستههای فرعی، اجازه انجام پردازشهای موازی، یعنی اجرای موثر چند وظیفه فرعی همزمان را به اسکریپت میدهند.
#!/bin/bash # ( #داخل پرانتزها، و بنابراین یک پوسته فرعی . . . while [ 1 ] #حلقه بیپایان. do echo "Subshell running . . ." done ) #اسکریپت برای همیشه، یا حداقل تا موقعی که #+به وسیله Ctl-C خاتمه بیابد، اجرا خواهد شد. exit $? #پایان اسکریپت (اما هرگز به اینجا نمیرسد).
bash$ sh subshell-test.sh
و ضمن اینکه اسکریپت در حال اجرا است، از یک xterm دیگر:
bash$ ps -ef | grep subshell-test.sh UID PID PPID C STIME TTY TIME CMD 500 2698 2502 0 14:26 pts/4 00:00:00 sh subshell-test.sh 500 2699 2698 21 14:26 pts/4 00:00:24 sh subshell-test.sh ^^^^تحلیل:
به طور کلی، یک فرمان خارجی در اسکریپت یک پردازش فرعی منشعب میسازد [1] در حالیکه یک Bash builtin چنین نمیکند. به این علت، فرمانهای داخلی به طور سریعتری اجرا میگردند و از معادلهای خارجیشان منابع سیستم کمتری مصرف میکنند.
یک لیست فرمان جاسازی شده در میان پرانتزها به صورت یک پوسته فرعی اجرا میگردد.
متغیرهای یک پوسته فرعی در خارج از بلوک کد پوسته فرعی قابل رویت نیستند. آنها در پردازش پدر، یعنی پوستهای که پوسته فرعی را راهاندازی نموده، قابل دستیابی نیستند. در واقع این متغیرها برای پردازش فرزند محلی هستند.
مثال 21-1. حوزه متغیر در پوسته فرعی
#!/bin/bash #subshell.sh echo echo "We are outside the subshell." echo "Subshell level OUTSIDE subshell = $BASH_SUBSHELL" #Bash نگارش 3، متغیر جدید $BASH_SUBSHELL را اضافه کرد. echo; echo outer_variable=Outer global_variable= #تعریف متغیر سراسری برای «ذخیرهسازی» مقدار متغیر پوسته فرعی. ( echo "We are inside the subshell." echo "Subshell level INSIDE subshell = $BASH_SUBSHELL" inner_variable=Inner echo "From inside subshell, \"inner_variable\" = $inner_variable" echo "From inside subshell, \"outer\" = $outer_variable" global_variable="$inner_variable" #آیا «بیرون بردن» متغیر از #+پوسته فرعی را ممکن میسازد؟ ) echo; echo echo "We are outside the subshell." echo "Subshell level OUTSIDE subshell = $BASH_SUBSHELL" echo if [ -z "$inner_variable" ] then echo "inner_variable undefined in main body of shell" else echo "inner_variable defined in main body of shell" fi echo "From main body of shell, \"inner_variable\" = $inner_variable" #$inner_variable به صورت تهی (ارزشگذاری نشده) نمایش خواهد یافت # +زیرا متغیرهای تعریف شده در پوسته فرعی «متغیرهای محلی» هستند. #آیا راه علاجی برای این مطلب وجود دارد؟ echo "global_variable = "$global_variable"" #چرا این عمل نمیکند؟ echo #================================================================== #به طور اضافی ... echo "-----------------"; echo var=41 #متغیر همگانی. ( let "var+=1"; echo "\$var INSIDE subshell = $var" ) #42 echo "\$var OUTSIDE subshell = $var" #41 #عملیات متغیر در داخل یک پوسته فرعی، حتی با یک متغیر «همگانی»، روی #+مقدار آن در خارج از پوسته فرعی تاثیر نمیکند! exit 0 #پرسش: #------------------- #بعد از خروج از یک پوسته فرعی، راهی جهت دوباره وارد شدن به همان #+پوسته فرعی واقعی جهت ویرایش یا دسترسی متغیرهای آن وجود دارد؟
همچنین $BASHPID و مثال 34-2 را ببینید.
تعریف: حوزه یک متغیر زمینهای است که آن متغیر دارای معنی است، که در آن زمینه دارای مقداری است که میتواند به آن مراجعه بشود. برای مثال، حوزه یک متغیر محلی فقط درون تابع، بلوک کد، یا پوسته فرعی واقع میشود که در آن تعریف گردیده، در حالیکه حوزه یک متغیر سراسری(همگانی) تمام آن اسکریپتی است که متغیر در آن ظاهر میگردد. |
در حالیکه متغیر داخلی $BASH_SUBSHELL نشاندهنده سطح درونی یک پوسته فرعی است، متغیر $SHLVL در داخل یک پوسته فرعی تغییری نشان نمیدهد.
echo " \$BASH_SUBSHELL outside subshell = $BASH_SUBSHELL" # |
تغییرات دایرکتوری انجام شده در پوسته فرعی به پوسته پدر منتقل نمیگردد.
مثال 21-2. لیست پروفایل کاربران
#!/bin/bash #چاپ پروفایل همه کاربران. #این اسکریپت به وسیله Heiner Steven نوشته شده و توسط نگارنده ویرایش گردیده. FILE=.bashrc #فایل محتوی پروفایل کاربر در اسکریپت اولیه .profile بود. for home in `awk -F: '{print $6}' /etc/passwd` do [ -d "$home" ] || continue #اگر دایرکتوری خانگی نیست به مورد بعدی برو. [ -r "$home" ] || continue #اگر قابل خواندن نیست به مورد بعدی برود. (cd $home; [ -e $FILE ] && less $FILE) done #وقتی اسکریپت خاتمه مییابد، نیاز به تعویض دایرکتوری به دایرکتوری اولیه نیست #+به دلیل اینکه cd $home در پوسته فرعی صورت میگیرد. exit 0
پوسته فرعی میتواند جهت تنظیم یک «محیط اختصاصی» برای گروهی از فرمانها به کار برود.
COMMAND1 COMMAND2 COMMAND3 ( IFS=: PATH=/bin unset TERMINFO set -C shift 5 COMMAND4 COMMAND5 exit 3 #فقط از پوسته فرعی خارج میشود! ) #پوسته پدر تحت تاثیر قرار نگرفته، و محیط محافظت میشود. COMMAND6 COMMAND7
همچنانکه در اینجا دیده میشود، فرمان exit فقط پوسته فرعی را که در آن اجرا میشود، نه پوسته پدر یا اسکریپت را، خاتمه میدهد.
یک کاربرد برای چنین «محیط اختصاصی»، بررسی نمودن آن است که آیا یک متغیر تعریف شده است.
if (set -u; : $variable) 2> /dev/null then echo "Variable is set." fi #متغیر در اسکریپت جاری تنظیم شده است، یا یک متغیر داخلی Bash #+است، یا در محیط حاضر است (صادر شده است). مترجم: در اینجا set -u باعث میگردد پوسته غیرمحاورهای هنگام اقدام به بسط متغیری که برقرار نیست یک پیغام خطا در stderr نوشته و بلافاصله خارج گردد. به دنبال آن : $variable قرار دارد و میدانیم که پوسته قبل از اجرای فرمان، پارامتر آن را بسط میدهد و در اینجا به علت تنظیم قبلی اگر متغیر برقرار نباشد وضعیت خروج از پوسته فرعی ناموفق و اگر برقرار باشد موفق خواهد بود. set -u معادل set -o nounset است. #هم میتوانست نوشته شود [[ ${variable-x} != x || ${variable-y} != y ]] #یا [[ ${variable-x} != x$variable ]] #یا [[ ${variable+x} = x ]] #یا [[ ${variable-x} != x ]]
یک کاربرد دیگر کنترل وجود یک فایل قفل است:
if (set -C; : > lock_file) 2> /dev/null then : #lock_file وجود ندارد: هیچ کاربری در حال اجرای اسکریپت نیست else echo "Another user is already running that script." exit 65 fiمترجم: تنظیم گزینه C (بزرگ) برای پوسته مانع از رونویسی شدن فایل موجود به وسیله عملگر تغییر مسیر > پوسته میگردد. پس از آن : > lock_file قرار دارد. این به معنای آن است که فایل نامبرده باز شود و نتیجه فرمان (که در اینجا هیچ است) در آن نوشته شود. اگر فایل موجود نباشد ایجاد میشود و بدون انجام عملی بقیه کار ادامه مییابد و اگر موجود باشد به علت تنظیم گزینه C رونویسی آن منع گردیده بنابراین با وضعیت خروج ناموفق از پوسته فرعی خارج گردیده و پس از نمایش پیغام تعیین شده، از اسکریپت نیز خارج میشود. set -C معادل set -o noclobber است. #قطعه کد نوشته Stéphane Chazelas با #+ویرایش Paulo Marcel Coelho Aragao.
+
پردازشها میتوانند به طور موازی در داخل پوستههای فرعی متمایز اجرا گردند. این قابلیت، تفکیک یک وظیفه پیچیده به اجزای متشکله فرعی را که به طور همزمان پردازش میشوند، میسر میکند.
مثال 21-3. اجرای پردازشهای موازی در پوستههای فرعی
(cat list1 list2 list3 | sort | uniq > list123) & (cat list4 list5 list6 | sort | uniq > list456) & #هر دو مجموعه لیستها را به طور همزمان ادغام و مرتب میکند. #اجرا در پسزمینه، اجرای موازی را تضمین مینماید. # #همان کار به صورت # # wait #تا خاتمه یافتن پوستههای فرعی، فرمان بعد اجرا نشود. diff list123 list456
تغییر مسیر ورودی-خروجی به یک پوسته فرعی، از عملگر لوله | همچون در ls -al | (command) استفاده میکند.
یک بلوک کد در میان براکتهای کمانی، پوسته فرعی راهاندازی نمیکند.
{ command1; command2; command3; . . . commandN; } var1=23 echo "$var1" # |
[1] | یک فرمان خارجی احضار شده با یک exec به طور معمول پردازش فرعی-پوسته فرعی منشعب نمیکند. |