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

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

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

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

پوسته فرعی

پوسته فرعی

یکی از مفاهیم اساسی برنامه‌نویسی پوسته پوسته فرعی می‌باشد.

در یک سیستم یونیکس هر پردازشی دارای سهم خود از حافظه می‌باشد، برای نگهداری متغیرهایش، توصیف‌گرهای فایل خودش، نسخه خودش از محیط به ارث برده از پردازش والدش، و مانند آن. تغییرات متغیرها (و سایر اطلاعات خصوصی)در یک پردازش بر هیچ یک از پردازشهای دیگری که به طور جاری در سیستم اجرا می‌شوند تأثیر نمی‌گذارد.

این مطلب موقع نوشتن اسکریپت‌ها اهمیت می‌یابد، زیرا مقدار بسیاری از کار توسط پردازشهای فرزندِ اسکریپت انجام می‌شود. موقعیت‌های آشکاری وجود دارد، مانند این:

perl -e '$ENV{"foo"}="bar"; system "echo foo is \$foo"'
echo "$foo"

برنامه perl به عنوان یک فرزند پوسته اجرا می‌شود، و بنابراین تغییر ایجاد شده توسط پرل برمتغیر foo محیط، فقط توسط پردازش فرزند پرل دیده می‌شود (آنکه به وسیله system فراخوانی گردیده)، اما توسط پردازش پدر پرل (اسکریپت مثال ما) خیر. اگر چه، آنچه به طور کلی از «پوسته فرعی» مورد نظر ما می‌باشد، این نیست.

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

این هم یک مثال از پوسته فرعی اجباری:

foo=old
(foo=bar)
echo "$foo"

پرانتزهای پیرامون فرمان دوم در اسکریپت، فرمان را مجبور می‌سازند در یک پوسته فرعی اجرا شود، و بنابراین تغییری که در متغیر foo ایجاد می‌گردد در اسکریپت اصلی دیده نمی‌شود.

این مورد می‌تواند فوق‌العاده مفید باشد. برای مثال، شاید شما بخواهید به طور موقت یک متغیر محیط را خاموش کنید:

(unset http_proxy; wget ...)

یا شاید یک وظیفه‌ای دارید که باید در یک دایرکتوری کاری خاص اجرا گردد، و می‌خواهید باقیمانده اسکریپت (یا پوسته محاوره‌ای شما) تحت تأثیر آن واقع نشود:

(cd /foo || exit 1; tar ...)

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

echo hello | read a
echo "$a"

در این مثال، توسط read یک مقدار به متغیر a تخصیص داده می‌شود، اما تنها در پردازش خودش. اگر ما در یک پوسته بورن، یک پوسته POSIX، pdksh، یا Bash باشیم، read در یک پوسته فرعی قرار می‌گیرد (زیرا هر فرمان داخل یک خط لوله در پوسته فرعی خودش اجرا می‌شود)، و از این جهت متغیر a در اسکریپت اصلی تأثیر نمی‌پذیرد.

اگر جه، در ksh88 یا ksh93، آخرین فرمان یک خط لوله در یک پوسته فرعی اجرا نمی‌شود. در آن پوسته‌ها، باعث می‌گردد متغیر a در اسکریپت اصلی مقدار hello در خود نگاه دارد.

جانشینی فرمان معمولاً فقط موقعی یک مشکل است که با توابع ترکیب می‌شود. برای مثال،

f() {
  count=$(($count+1))
  echo something
}
count=0
value=$(f)
echo "$count"

متغیر سراسری count هر وقت تابع f احضار می‌شود، افزایش داده می‌شود. با این وجود، وقتی f به طوریکه در بالا نشان داده شده در یک جایگزینی فرمان فراخوانی می‌شود، تابع در یک پوسته فرعی اجرا می‌گردد. این یعنی، چنانکه من اطمینان دارم تا کنون حدس زده‌اید، متغیر counter در بخش اصلی اسکریپ افزایش داده نمی‌شود.

برای راه‌های رفع و رجوع این قبیل مشکلات،پرسش و پاسخ شماره ۲۴ را ببینید.

برای یک مثال از تفاوت میان یک پوسته فرعی و یک پردازش فرزندی که اتفاق می‌افتد یک پوسته باشد:

unset a; a=1
(echo "a is $a in the subshell")
sh -c 'echo "a is $a in the child shell"'

در پوسته فرعی، متغیر a معمولی پوسته قابل رؤیت است، اما چون export نگردیده است، پردازش فرزند کامل آن را نمی‌بیند.


CategoryShell

پوسته فرعی (آخرین ویرایش ‎2012-01-19 18:18:29‎ توسط GreyCat)