پايپها و انتقال
پيش از آنکه در اين شماره بهطور جدي وارد مبحث برنامهنويسي در پوسته( shell ) شويم، بهتر است ابتدا با شيوههاي انتقال ورودي و خروجي آشنا شويم.
انتقال خروجي
ممکن است تا کنون به چنين دستوري برخورده باشيد:
ls -l > lsoutput.txt$
اين دستور، خروجي دستور ls را داخل فايلي بهنام lsoutput.txt ميريزد. با عملگر> تقريبا همه خروجيها را ميتوان منتقل کرد. و اگر فايل مقصد از قبل وجود داشته باشد، در اين صورت فايل جديد جايگزين آن خواهد شد. براي اينکه خروجي را به انتهاي فايل اضافه کنيم، اما فايل قبلي پاک نشود، از عملگر> استفاده کنيد:
$ls << lsoutput.txt
اين دستور، نتيجه را به انتهاي فايل قبلي اضافه خواهد کرد.
انتقال ورودي
درست مانند انتقال خروجي دستورات، ميتوان وروديها را هم منتقل کرد و از ورودي استاندارد استفاده نکرد. براي نمونه:
more > lsoutput.txt$
البته اين مثال، بسيار جزيي و اندک بود و انتقال جريان ورودي و خروجي نسبت به ويندوز، ابعاد بسيار گستردهتري دارد.
پايپها
ميتوانيد پروسسها را با عملگر پايپ (1) بههمديگر وصل کنيد. برخلاف سيستم عامل داس، در لينوکس ميتوان بهکمک پايپها، پروسسها را همزمان با هم اجرا کرد و بهطور خودکار با انتقال جريان داده در ميان آنها، زمانبندي کرد. يک مثال ساده: ميتوانيد بهسادگي خروجي حاصل از دستور ps را بهدستور sort بدهيد. اگر از پايپها استفاده نکنيد، دستور چند گام به درازا خواهد کشيد:
ps < psout.txt$
<$sort psout.txt pssort.out
حالا راه بهتر و تر و تميزتر اين است که اين دو پروسس را با يک پايپ به هم وصل کنيم:
ps1 sort < pssort.out$
لابد ميخواهيد خروجي کار هم، صفحهبندي شده باشد، پس حتما از دستور سومي استفاده خواهيد کرد بهنام more و آن را با پايپ به دو دستور قبلي وصل ميکنيد:
$ps1 sort 1 more
در عمل هيچ محدوديتي براي تعداد پروسسهايي که بههم وصل ميشوند، وجود ندارد. فرض بگيريم که ميخواهيد نام تمام پروسسهايي که در حال اجرا هستند بهجز پوسته را ببينيد. اين خط دستور را نگاه کنيد:
$ps-xo comm1 sort 1 uniq
grep -v sh1 more
اين دستور چه کار ميکند؟
خروجيps را ميگيرد، به ترتيب الفبا مرتب ميکند، پروسسها را با استفاده ازuniq استخراج ميکند، با استفاده از grep -v sh پروسسي که اسمش sh است را حذف ميکند و دست آخر بهصورت صفحهبندي شده به خروجي (نمايشگر) ميفرستد. اين روش، همانطور که شما هم ميبينيد، عجب روشتر و تميزي است براي پيوندانيدن چند دستور بههمديگر! تازه بدون اينکه به دردسر بيافتيم و فايلهاي موقت ايجاد بکنيم! هرچند، حسابي مراقب يک چيز باشيد: اگر رشتهاي از دستورها داشتيد و قرار بود خروجي را به يک فايل بفرستيد، هيچ وقت از يک نام فايل دوبار در رشته دستوري استفاده نکنيد. براي مثال اگر چنين دستوري بنويسيد:
cat mydata.txt 1 sort 1 uniq < mydata.txt$
در اينصورت با يک فايل خالي مواجه خواهيد شد، چون پيش از اينکه بخواهيد فايلmydata.txt را بخوانيد، آن را بازنويسي کردهايد.
پوسته يا يک زبان برنامهنويسي
حالا که ديگر برخي از عمليات پايه پوسته را ياد گرفتيم، وقت آن رسيده است که سراغ برنامههاي واقعي پوسته برويم. دو راه براي نوشتن برنامههاي پوسته وجود دارد. ميتوانيد رشتهاي از دستورات را بنويسيد و به پوسته اجازه بدهيد که آنها را تکتک اجرا کند، يا دستورات را داخل فايل بريزيد و بعد بهعنوان برنامه از آن استفاده کنيد.
برنامههاي تعاملي
(Interactive Programs)
نوشتن و اجرا کردن دستورها، يکي از راههاي سريع و آسان آزمايش تکههاي کوچک کد است، براي يادگرفتن مناسب. فرض بگيريد تعداد زيادي فايلC داريد و ميخواهيد فايلهايي که شامل رشتهPOSIX هستند را شناسايي کنيد، بهجاي اينکه تکتک و از طريق دستور grep بخواهيم اين کار را انجام دهيم، ميتوانيم همه اينکارها را داخل اسکريپتي تعاملي بنويسيم، اين پايين را نگاه کنيد:
for file in *$
do <
if grep -l POSIX $file<
then<
more $file<
fi<
done<
نگاه کنيد که چقدر طبيعي $ برنامه پوسته به < تبديل ميشود و پوسته منتظر اطلاعات بعدي است. ميتوانيد تايپ کردن را بس کنيد و به پوسته اجازه بدهيد خودش تصميم بگيرد که شما کارتان تمام شده است و بعد اسکريپت فورا اجرا ميشود. پوسته همچنين ازWildcard expansionها نيز پشتيباني ميکند. ديگر قريب بهيقين شما ميدانيد که * يعنيWildcard براي همه کاراکترها. ممکن است اين را ندانيد که براي يک حرف نيزWildcard بهنام داريم، همچنين]set[ مجموعهاي از کاراکترها را در خود ميگيرد که قرار است چک شوند. برعکس آن اگر 8 بيايد، مثل]set[ در اين صورت، هر چيزي را شامل ميشود جز آنهايي که داخل براکتاند. نشان آکولاد ت ت هم به شما اجازه ميدهد تا چند رشته را در مجموعهاي کنار هم قرار دهيد تا کار خاصي انجام بدهيد:
ls my- تfinger, toeتs$
اين دستور فايلهاي myfingers و mytoes را ليست ميکند. اين دستور از پوسته استفاده ميکند تا هر فايلي که داخل دايرکتوري فعلي است را بررسي کند.
ساختن يک اسکريپت
با هر ويرايشگر متني که ميلتان ميکشد، ميتوانيد فايلي شامل دستورات بسازيد، فايلي بسازيد بهنام first که شبيه مثال زير باشد:
/bin/shَ#
firstَ#
This file looks throughَ#
all the files in the current
directory for the string POSIX,َ#
and then prints the names of
those files to the standard output.َ#
for file in *
do
file$if grep -q POSIX
then
file$echo
fi
done
0exit
توضيحات(comment) ها با نماد آغاز ميشوند و تا پايان خط بهعنوان توضيح بهحساب ميآيند؛ هر چند عرف است که در ستون اول هميشه بگذارند. حال در خط بعدي به دستور َbin/sh/! بر ميخوريم که فرم خاصي از توضيح است. کاراکترهاي َ به سيستم ميگويند که آرگومانهايي که در ادامه ميآيند، برنامهاي است که براي اجراي اين فايل بايد دنبال شودbin/sh/ . برنامه پوسته پيشفرض است.
نکته: مسير پس از َ مسير قطعي(Absolute) است. با هم توافق کردهاند که اين رشته را از 32 کاراکتر کمتر نگهدارند تا با سيستمهاي قديميتر همخوان باشد، چون برخي از يونيکسهاي قديمي از تعداد محدودي از کاراکترهايي که بعد از ! ميآيند، بهره ميبردند. واضح است که لينوکسهاي امروزي ديگر اين محدوديت را ندارند. از آنجايي که اسکريپت وارده، بهعنوان ورودي استانداردي در پوسته مطرح است. در اين صورت ميتواند شامل هر دستوري باشد که متغير محيطي PATH شما مسير آن را در خود دارد.
دستورexit در انتهاي اسکريپت اطمينان ميدهد که اسکريپت با کد خروجي مطمئن کنترل اجراي دستورات را به سيستم عامل بر ميگرداند. هرچند که بهندرت چنين چيزي چک ميشود، اما ميشود فهميد که آيا اسکريپت با موفقيت اجرا شده است يا خير. در اين صورت ميتوان با اطمينان از اين اسکريپت در اسکريپت يا دستوري ديگر استفاده کرد و از صحت اجراي آن اطمينان حاصل کرد. حتا اگر نخواهيد اسکريپتتان را بقيه اجرا کنند، باز هم در انتهاي اسکريپت اين کد را بگذاريد که کاري منطقي است. به کاربردي بودن اسکريپت خود اعتقاد داشته باشيد. فرض بگيريد که شايد، يک در يک ميليون قرار باشد بهعنوان بخشي از اسکريپتي ديگر بهکار گرفته شود.
نکته: عدد صفر نشانگر خروج موفقيتآميز است.
اجرايي کردن يک اسکريپت
حالا فايل اسکريپتمان را ساختيم، ميتوانيم به دو صورت آن را اجرا کنيم. راه سادهتر اين است که اسم فايل را به پوسته بدهيم.
/bin/sh first $
اين دستور بايد عمل کند، اما چقدر خوب ميشود که اسکريپت را فقط با صدا کردن نامش استفاده کنيم و از اينbin/sh/ خلاص شويم. اين کار را با تغيير حالت فايل (chmod) بهسادگي ميتوان انجام داد:
$chmod+xfirst
اين دستور، فايل first را با +x اجرايي ميکند. و پس از اين، تنها کافي است با دستور زير اسکريپت را اجرا کنيد:
$first
اگر پيغامي گرفتيد که "چنين دستوري يافت نشد!" متغير محيطي PATH احتمالا به مسير فايلهاي اسکريپت شما اشاره نميکند. براي رفع اين مشکل، يا تايپ کنيدPATH=$PATH: يا فايل bash-profile را ويرايش کنيد و اين دستور را به انتهاي آن بيفزاييد. سپس يک بارlog out کنيد و دوباره به سيستم برگرديد. اين هم نشد،first/. را در دايرکتورياي که اسکريپت داخلش است، بنويسيد تا مسير کامل نسبي به اسکريپت را به فايل بدهيد.
استفاده از ./ يک سود ديگر هم دارد، آن اين است که شما تصادفا دستور ديگري با اين نام را اجرا نخواهيد کرد. اگر ديديد که اسکريپت شما با موفقيت اجرا شد، ميتوانيد به جاي مطمئني منتقلش کنيد، اگر از اين دستور فقط خودتان استفاده ميکنيد، ميتوانيد يک دايرکتوري bin در فولدر home خود بسازيد و مسيرش را بهPATH اضافه کنيد. اگر ميخواهيد بقيه کاربران هم از آن استفاده کنند، آن را داخل فولدرusr/local/bin/ قرار دهيد. اگر دسترسيRoot نداريد، بايد از سرپرست سيستم درخواست کنيد اين کار را براي شما انجام دهد. اگر هم مايل نيستيد که کسي آن را تغيير دهد، ميتوانيد دسترسي نوشتن در آن فايل را برداريد. دستوراتي که سرپرست سيستم طي آن مالکيت و دسترسيهاي نرمافزار را مشخص ميکند، چيزي شبيه اين خواهد بود:
cp first /usr/local/binَ#
chown root /usr/local/bin/firstَ#
chgrp root /usr/local/bin/firstَ#
َchmod557 /usr/local/bin/first #
توجه داشته باشيد که از شکل کلي دستور chmod استفاده کرديم، چون دقيقا ميدانستيم که چه سطح دسترسياي ميخواهيم بدهيم. اما شما ميتوانيد از شکل طولانيتر، اما شايد واضحتر دستور chmod استفاده کنيد:
َchmod u=rwx,go=rx /usr/local/bin/first #
براي اطلاعات بيشتر از راهنماي دستور chmod استفاده کنيدman chmod :