آغاز یک پروژه در اسکریپتنویسی
در این درس نحوه ایجاد یک برنامه را آغاز خواهیم کرد. هدف این پروژه یادگیری یک برنامه خاص نیست بلکه این است که ببینیم چگونه ویژگیهای مختلف شل (Shell) را میتوان به منظور ایجاد برنامهها استفاده کرد و از همه مهمتر اینکه برنامههای خوبی ایجاد کنیم.
برنامهای که ما ایجاد خواهیم کرد یک برنامه گزارشساز است. این برنامه آمارهای مختلف درباره سیستم ما را جمعآوری کرده و در قالب یک فایل HTML به ما گزارش میدهد. به این صورت به راحتی میتوانیم گزارش ایجاد شده را درون مرورگر ببینیم. برنامهها معمولا در طی مراحلی ایجاد میشوند که در طی این مراحل ویژگیها و قابلیتهای مختلف به برنامه اضافه میشوند. اولین مرحله از ایجاد برنامه یک صفحه بسیار کوچک HTML ایجاد میکند که دارای هیچ اطلاعات سیستمی نمیباشد. در مراحل بعدی این اطلاعات اضافه خواهند شد.
مرحله اول: ایجاد یک سند حداقلی
اولین چیزی که بایستی بدانیم این است که یک سند ساده HTML چگونه شکل گرفته است. این سند به صورت زیر می باشد.:
<HTML>
<HEAD>
<TITLE>Page Title</TITLE>
</HEAD>
<BODY>
Page body.
</BODY>
</HTML>
اگر که این متن را وارد ویرایشگر متنی خود کنیم و آن را با نام foo.html ذخیره کنیم، میتوانیم از مسیر زیر در مرورگر خود به قابل دسترسی پیدا کنیم:
file:///home/username/foo.html
اولین مرحله از ایجاد برنامه ما این خواهد بود که بتوانیم این فایل HTML را درون ورودی استاندارد نمایش دهیم. ما میتوانیم یک بر نامه بنویسیم که این کار را به سادگی انجام دهد. ویرایشگر متن خود را باز کنید و درون پوشه bin قابلی با نام ~/bin/sys_info_page ایجاد کنید. شما برای ویرایش متن میتوانید از ویرایشگرهای مختلف و پیشرفتهای مثل vim استفاده کنید:
[me@linuxbox ~]$ vim ~/bin/sys_info_page
سپس کدهای زیر را درون ویرایشگر وارد کرده و فایل را ذخیره میکنم:
#!/bin/bash # Program to output a system information page echo "<HTML>" echo " <HEAD>" echo " <TITLE>Page Title</TITLE>" echo " </HEAD>" echo " <BODY>" echo " Page body." echo " </BODY>" echo "</HTML>"
خط اول حاوی توالی شیبنگ (#!) میباشد سپس یک کامنت را در خط دوم داریم. سپس هر کدام از خطوط بعدی متن محتوای HTML ما را echo میکنند. پس از ذخیره فایل بایستی فایل را به یک فایل اجرایی تبدیل کنیم و سپس اجرای آن را آزمون کنیم:
[me@linuxbox ~]$ chmod 755 ~/bin/sys_info_page [me@linuxbox ~]$ sys_info_page
زمانیکه برنامه اجرا میشود بایستی متن محتوای HTML را بر روی صفحه نمایش ببینیم چرا که فرمانهای echo موجود در اسکریپت، خروجی خود را وارد ورودی استاندارد میکنند.
[me@linuxbox ~]$ sys_info_page > sys_info_page.html [me@linuxbox ~]$ firefox sys_info_page.html
دوباره برنامه را اجرا خواهیم کرد و این بار خروجی را به درون فایل sys_info_page.html ارسال میکنیم تا بتوانیم نتایج را درون مرورگر مشاهده کنیم:
#!/bin/bash
# Program to output a system information page
echo "<HTML>
<HEAD>
<TITLE>Page Title</TITLE>
</HEAD>
<BODY>
Page body.
</BODY>
</HTML>"
وقتیکه برنامهای را مینویسیم ایده خوبی است که تلاش کنیم همه چیز را ساده و واضح کنیم. در نتیجه زمانی که یک برنامه خواناتر باشد نگهداری و درک آن نیز آسانتر خواهد بود. نسخه فعلی برنامه ما خوب است ولی میتوان آن را سادهتر نوشت. میتوانیم همه فرمانهای echo را به یک فرمان تبدیل کنیم که اضافه کردن خطوط دیگر را آسانتر خواهد کرد. به همین منظور برنامه را به شکل زیر تغییر میدهیم:
[me@linuxbox ~]$ echo "<HTML> > <HEAD> > <TITLE>Page Title</TITLE> > </HEAD> > <BODY> > Page body. > </BODY> > </HTML>"
مرحله دوم: افزودن داده
اکنون که برنامه ما قادر به ایجاد یک سند حداقلی است. بیایید کمی داده درون گزارش خود قرار دهیم. به این منظور بایستی تغييرات زیر را اعمال کنیم:
#!/bin/bash
# Program to output a system information page
echo "<HTML>
<HEAD>
<TITLE>System Information Report</TITLE>
</HEAD>
<BODY>
<H1>System Information Report</H1>
</BODY>
</HTML>"
ما در اینجا یک عنوان و یک تگ ۱ به بدنه گزارش اضافه کردیم.
متغیرها و ثابتها (Variables and Constants)
یک مشکل در سند اسکریپت ما وجود دارد. می بینید که رشته System Information Report در کد ما تکرار شده است. در یک سند کوچک مثل این چنین تکراری یک مشکل محسوب نمیشود ولی فرض کنید که اسکریپت ما بسیار طولانی بود و ما چندین نمونه از این رشته را در جای جای که خود داشتیم. خوب حالا تصمیم به تغییر این رشته میگیریم! چه اتفاقی خواهد افتاد. بایستی به صورت دستی تک تک جاهایی که این رشته را به کار بردیم تغییر دهیم. این کار زمان بر خواهد بود. حالا اگر اسکرپیت را به گونهای طراحی کنیم که این رشته فقط یک بار به کار رود چطور؟ این باعث میشود که نگهداری فایل اسکریپت در آینده به مراتب آسانتر شود. این کار را به صورت زیر و با تعریف متغیر انجام میدهیم:
#!/bin/bash
# Program to output a system information page
title="System Information Report"
echo "<HTML>
<HEAD>
<TITLE>$title</TITLE>
</HEAD>
<BODY>
<H1>$title</H1>
</BODY>
</HTML>"
با ایجاد یک متغیر با نام System Information Report و اختصاص آن به مقدار System Value Report دیگر فقط نام متغیر را در جاهای مختلف به کار خواهیم برد و هر زمان که خواستیم مقدار متغیر را تغییر دهیم تغییر فقط در یک منطقه صورت خواهد پذیرفت.
ایجاد متغیرها و ثابتها
اکنون چگونه یک متغیر ایجاد کنیم؟ بسیار ساده با فقط از آن استفاده میکنیم و زمانی که شل با یک متغیر مواجه میشود. به صورت خودکار آن را ایجاد میکند. در بسیاری از زبانهای برنامهنویسی شما بایستی به صورت اختصاصی متغیر را تعریف کنید و بایستی قبل از استفاده تعریف شده باشد. شل در این باره بسیار ریلکس عمل میکنند و همین موضوع ممکن است ما را با مشکلاتی مواجه کند. برای مثال سناریوی زیر را در نظر بگیرید:
[me@linuxbox ~]$ foo="yes" [me@linuxbox ~]$ echo $foo yes [me@linuxbox ~]$ echo $fool [me@linuxbox ~]$
ما ابتدا مقدار yes را به متغیر foo اختصاص دادیم و سپس مقدار آن را با استفاده از فرمان echo چاپ کردیم. سپس این بار مقدار آن را به صورت غلط یعنی $foo وارد کردیم و نتیجهای که دریافت میکنیم یک مقدار خالی است. دلیل آن این است که شل (Shell) با خوشحالی تمام متغیر fool را ایجاد کرده و مقدار هیچ چیز را به آن اختصاص میدهد. از این مثال یاد می گیریم که بایستی حتما دقت زیادی به تلفظ و نوشتن دقیق متغیرها داشته باشیم. با توجه به مسایلی که درباره بسطها در شل (Shell) آموختیم میدانیم که فرمان echo $foo دستخوش بسط میشود و می شود echo yes. از طرف دیگر فرمان echo $foo بسط پیدا میکند و یک echo خالی را ایجاد میکند. این مسئله ممکن است در فرمانهایی که نیازمند آرگومان هستند باعث خرابی شود. برای مثال:
[me@linuxbox ~]$ foo=foo.txt [me@linuxbox ~]$ foo1=foo1.txt [me@linuxbox ~]$ cp $foo $fool cp: missing destination file operand after `foo.txt' Try `cp --help' for more information.
ما در اینجا مقادیری را به دو متغیر foo و foo1 اختصاص دادیم. پس از آن یک فرمان cp را اجرا کردیم ولی در اجرای این فرمان نام متغیر دوم را اشتباها به جای foo1 به صورت fool تایپ کردیم. پس از بسط فرمان cp فقط یک آرگومان دریافت میکند در صورتی که نیازمند دو آرگومان است. برخی قوانین برای اسامی متغیرها وجود دارد:
- اسامی متغیرها ممکن است شامل کاراکترهای حروف الفبایی و عددی اعداد و حروف، و همچنین زیرخط (علامت Underline) باشد.
- اولین کاراکتر نام متغیر بایستی یک حرف یا Underline باشد و نمیتواند عدد باشد.
- فاصله و نشانههای نقطهگذاری در نام متغیر مجاز نیستند.
کلمه متغیر تکیه بر این موضوع دارد که مقدار آن قابل تغییر است و در بسیاری از اپلیکیشنها متغیرها به همین نحو استفاده میشوند. هر چند متغیر در اپلیکیشن ما یمنی همان title به عنوان یک ثابت استفاده شده است. یک ثابت درست مثل یک متغیر است از این نظر که دارای نام و محتوای خاصی است. تفاوت بین ثابت و متغیر از نام آنها پیداست. مقدار ثابت هرگز تغییر نمیکند. در اپلیکیشنی که محاسبات جغرافیایی را انجام میدهد. ممکن است بخواهیم PI ( عدد پی) را به عنوان یک ثابت تعریف کنیم و آن را به مقدار حقیقی آن یعنی ٣,١٤١٥ اختصاص دهیم به جای اینکه دائما این عدد را در طی برنامه استفاده کنیم. شل (Shell) هیچ تفاوتی بین متغیرها و ثابتها قائل نیست! این عبارات اکثرا برای راحتی برنامهنویسان آینده به کار میرود. یک قرار رایج این است که از حروف بزرگ برای ثابتها و از حروف کوچک برای متغیرها استفاده کنیم تا بین آنها تمیز قائل شویم. به همین منظور سند اسکریپت خود را به صورت زیر تغییر میدهیم:
#!/bin/bash
# Program to output a system information page
TITLE="System Information Report For $HOSTNAME"
echo "<HTML>
<HEAD>
<TITLE>$TITLE</TITLE>
</HEAD>
<BODY>
<H1>$TITLE</H1>
</BODY>
</HTML>"
همچنین فرصت این را پیدا کردیم تا عنوان خود را با اضافه کردن متغیر HOSTNAME کمی بهینه کنیم. این متغیر شامل نام شبکه ماشین میباشد که به صورت خودکار در ماشین تعریف میشود.
تخصیص مقدار به متغیرها و ثابتها
این جایی است که دانش ما درباره بسطها خود را نشان میدهد. همانگونه که دیدیم متغیرها مقدار را به این شیوه دریافت میکنید:
variable=value
در حالیکه variable نام متغیر و value یک رشته و مقدار متغیر است. برخلاف برخی دیگر زبانهای برنامهنویسی, شل (Shell) اهمیتی به نوع داده اختصاص داده شده به متغیر نمیدهد. در این حالت همه این مقادیر به عنوان رشته در نظر گرفته میشوند. ما میتوانیم با فرمان declare و گزینه -i شل (Shell) را مجبور کنید تا فقط مقادیر عدد صحیح را دریافت کند ولی این کار به ندرت انجام میشود. توجه داشته باشید که در تخصیص آن نبایستی هیچ فاصلهای بین نام متغیر، علامت مساوی و مقدار باشد. خوب مقدار متغیر شامل چه چیزی میتواند باشد؟ هر چیزی که بتوانیم آن را به رشته بسط دهیم:
a=z # Assign the string "z" to variable a. b="a string" # Embedded spaces must be within quotes. c="a string and $b" # Other expansions such as variables can be # expanded into the assignment. d=$(ls -l foo.txt) # Results of a command. e=$((5 * 7)) # Arithmetic expansion. f="\t\ta string\n" # Escape sequences such as tabs and newlines.
چندین اختصاص متغیر را میتوان در یک خط انجام داد:
a=5 b="a string"
در طی بسط نامهای متغیرها را ممکن است بخواهیم با بریس () محصور کنیم. این کار در مواردی مفید است که نام یک متغیر به دلیل وجود محتوا در اطرافش چند پهلو شود. در اینجا سعی میکنیم تا با استفاده از یک متغیر نام یک فایل را از myfile به myfile1 تغییر دهیم:
[me@linuxbox ~]$ filename="myfile" [me@linuxbox ~]$ touch $filename [me@linuxbox ~]$ mv $filename $filename1 mv: missing destination file operand after `myfile' Try `mv --help' for more information.
این تلاش با شکست مواجه میشود. چرا ؟ به این دلیل که شل (Shell) دومین آرگومان فرمان mv را به عنوان یک متغیر جدید و خالی تفسیر میکند. ولی برای آن راه حلی وجود دارد:
[me@linuxbox ~]$ mv $filename ${filename}1
به این صورت با اضافه کردن پرانتزها دور متغیر مطمئن میشویم که شل دیگر عدد ۱ را به عنوان بخشی از نام متغیر تفسیر نمیکند. از این فرصت به منظور اضافه کردن برخی داده به گزارش خود استفاده میکنیم. این دادهها شامل تاریخ و ساعتی که گزارش ایجاد میشوند و نام کاربری سازنده گزارش است:
#!/bin/bash
# Program to output a system information page
TITLE="System Information Report For $HOSTNAME"
CURRENT_TIME=$(date +"%x %r %Z")
TIME_STAMP="Generated $CURRENT_TIME, by $USER"
echo "<HTML>
<HEAD>
<TITLE>$TITLE</TITLE>
</HEAD>
<BODY>
<H1>$TITLE</H1>
<P>$TIME_STAMP</P>
</BODY>
</HTML>"
اسناد رو بگیر (Here Document)
ما به دو نوع متد مختلف خروجی دادن متن با استفاده از فرمان echo نگاه کردیم. یک راه سومی هم وجود دارد که here document نامیده میشود.
شیوه here document یک شکل جدیدی از هدایت I/O میباشد. از چه نظر؟ از این نظر که یک بدنه متن را به درون اسکریپت خود جاساز میکنند و آن را به درون ورودی استاندارد یک دستور تزریق میکند. به این صورت کار میکند:
command << token
text
token
که در اینجا command نام فرمانی است که استاندارد ورودی را دریافت میکنند و token رشتهای است که به منظور نشان دادن پایان متن جاساز استفاده میشود. اکنون اسکریپت خود را به منظور استفاده از یک here document ویرایش میکنیم:
#!/bin/bash
# Program to output a system information page
TITLE="System Information Report For $HOSTNAME"
CURRENT_TIME=$(date +"%x %r %Z")
TIME_STAMP="Generated $CURRENT_TIME, by $USER"
cat << _EOF_
<HTML>
<HEAD>
<TITLE>$TITLE</TITLE>
</HEAD>
<BODY>
<H1>$TITLE</H1>
<P>$TIME_STAMP</P>
</BODY>
</HTML>
_EOF_
این بار به جای استفاده از دستور echo اسکریپت ما از فرمان cat و یک here document استفاده میکند. رشته _EOF_ به معنی پایان فایل، را به عنوان توکن استفاده میکنیم و متن خود را مشخص میکنیم. توجه کنید که توکن بایستی به صورت تنها در خط ظاهر شود و نباید پس از آن هیچ فاصلهای قرار داده شود.
خوب حالا فایده استفاده از here document چیست؟ تقریبا همان کار فرمان echo را انجام میدهد به جز اینکه به صورت پیشفرض تک کوتیشن و دابل کوتیشن درون here document معنی خاص خود درون شل (Shell) را از دست میدهند. به یک مثال در خط فرمان دقت کنید:
[me@linuxbox ~]$ foo="some text" [me@linuxbox ~]$ cat << _EOF_ > $foo > "$foo" > '$foo' > \$foo > _EOF_ some text "some text" 'some text' $foo
همانگونه که می بینیم شل (Shell) هیچ توجهی به علامتهای کوتیشن نشان نمیدهد و با آنها مثل کاراکترهای عادی رفتار میکند. این به ما این اجازه را میدهد تا کوتیشنها را آزادانه درون یک سند استفاده کنیم و این میتواند در سند ما هم مفید واقع شود.
here document را میتوان با هر فرمانی که ورودی استاندارد را قبول میکند استفاده کرد. در این مثال ما از یک here document به منظور پاس دادن یک سری از فرمانها به برنامه ftp به منظور دریافت فایل از سرور ریموت استفاده میکنیم:
#!/bin/bash # Script to retrieve a file via FTP FTP_SERVER=ftp.nl.debian.org FTP_PATH=/debian/dists/lenny/main/installer-i386/current/images/cdrom REMOTE_FILE=debian-cd_info.tar.gz ftp -n << _EOF_ open $FTP_SERVER user anonymous me@linuxbox cd $FTP_PATH hash get $REMOTE_FILE bye _EOF_ ls -l $REMOTE_FILE
اگر که ما عملگر هدایت را از << به <<- تغییر دهیم. شل کاراکترهای مقدم تب در here document را نادیده خواهد گرفت. این کار باعث میشود که یک سند here document را بتوان به همراه فرورفتگی قرار داد که باعث افزایش خوانایی متن میشود:
#!/bin/bash
# Script to retrieve a file via FTP
FTP_SERVER=ftp.nl.debian.org
FTP_PATH=/debian/dists/lenny/main/installer-i386/current/images/cdrom
REMOTE_FILE=debian-cd_info.tar.gz
ftp -n <<- _EOF_
open $FTP_SERVER
user anonymous me@linuxbox
cd $FTP_PATH
hash
get $REMOTE_FILE
bye
_EOF_
ls -l $REMOTE_FILE
درباره فرشید نوتاش حقیقت
همیشه نیازمند یک منبع آموزشی فارسی در حوزه نرمافزارهای آزاد/ متنباز و سیستمعامل گنو/لینوکس بودم. از این رو این رسالت رو برای خودم تعریف کردم تا رسانه «محتوای باز» رو بوجود بیارم.
نوشتههای بیشتر از فرشید نوتاش حقیقتاین سایت از اکیسمت برای کاهش جفنگ استفاده میکند. درباره چگونگی پردازش دادههای دیدگاه خود بیشتر بدانید.
دیدگاهتان را بنویسید