4. متغیرها- قسمت اول
دقیقاً مانند هر زبان برنامهنویسی موجود، مفهوم متغیرها - یک نام نمادین به محلی در حافظه است که میتوانیم کمیتهایی را در آنجا ذخیره کنیم، بخوانیم و محتویات آن را دستکاری کنیم. پوسته بورن هم استثناء نیست، و دراین بخش کلیات آن ارائه میشود . در بخش متغیرها- قسمت دوم که به متغیرهایی که برای استفاده محیط تنظیم گردیدهاند، نگاه میکنیم، بیشتر به آن پرداخته شده است
بیاید به عقب، به اولین مثال Hello World، نگاه کنیم. این مثال میتواند با استفاده از متغیرها اجرا شود (گرچه به اندازهای ساده است که در حقیقت ضرورتی ندارد! )
توجه کنید که در اطراف علامت "=" فاصلهای نباید باشد: VAR=value
کار میکند، اما VAR = value
کار نمیکند .
در حالت اول پوسته علامت "=" را میبیند وبه عنوان دستور واگذاری با آن رفتار میکند .
درحالت دوم شل گمان میکند که کلمه VAR باید نام دستوری باشد که میباید آن را اجرا کند.
اگر در باره آن فکر کنید، قابل فهم است - به چه طریق دیگر میتوانستید به شل بگویید که فرمان VAR را با "=" به عنوان اولین شناسه و "value" به عنوان دومین شناسه، اجرا کند ؟
کد زیر را در فایل var1.sh وارد کنید :
var.sh
#!/bin/sh
MY_MESSAGE="Hello World"
echo $MY_MESSAGE
این کد رشته "Hello World" را به متغیر
MY_MESSAGE
تخصیص میدهد و بعد دستور echo
مقدار این متغیر را نمایش میدهد .
توجه کنید که به علامتهای نقلقول در اطراف رشته Hello World احتیاج داریم . در حالیکه با دستور echo Hello World
میتوانستیم آنها را به کار نبریم، چون که echo هر تعداد پارامتر را میپذیرد، و یک متغیر فقط یک کمیت را ذخیره میکند، بنابراین یک رشته در بر دارنده کاراکتر فاصله باید نقلقولی بشود، تا پوسته بداند که با تمام آن به عنوان یک کمیت رفتار کند . در غیر اینصورت شل پس از واگذاری
MY_MESSAGE=Hello
سعی میکند دستور World
را اجراکند .
شل نگران نوع متغیرها نمیباشد،متغیرها میتوانند، رشتهها، اعداد صحیح، اعداد حقیقی، یا هر چیز دیگری که بخواهید را ذخیره کنند .
کسانی که از پرل استفاده میکنند شاید از این مطلب خیلی خوشحال شوند، اگر شما با C، پاسکال، یا بدتر با ada رشد کردهاید، ممکن است این کاملاًعجیب به نظر بیاید .
در حقیقت، همه اینها به صورت رشتهها ذخیره میشوند، اماروالهایی که انتظار یک عدد را دارند، میتوانند با اینها آنگونه رفتار کنند .
اگر یک رشته را به یک متغیر اختصاص دهید و بعد سعی کنید 1 را به آن اضافه کنید، نمیتوانید از نوع آن پرهیز کنید :
$ x="hello"
$ y=`expr $x + 1`
expr: non-numeric argument
$
چون برنامه بیرونی expr
فقط انتظار اعداد را دارد.
لیکن بین آن دو تمایز دستور زبانی وجود ندارد :
MY_MESSAGE="Hello World"
MY_SHORT_MESSAGE=hi
MY_NUMBER=1
MY_PI=3.142
MY_OTHER_PI="3.142"
MY_MIXED=123abc
توجه نمایید که، به هر حال، برای اجتناب از تفسیر ویژه توسط شل، کاراکترهای خاص باید به طور صحیح پوشانده شوند .
این مورد در بخش کاراکترهای گریز بیشتر بحث شده است.
میتوانیم با استفاده از دستور read
متغیرها را به صورت محاورهای مقداردهی کنیم،
اسکریپت بعدی نام شما را میپرسد و سپس به طور اختصاصی خوشآمد میگوید :
var2.sh
#!/bin/sh
echo What is your name?
read MY_NAME
echo "Hello $MY_NAME - hope you're well."
Mario Bacinsky به طور دوستانه به من منعکس کرد که- من به طور معمول این علامتهای نقلقول دوگانه در سطر سوم را از قلم میاندازم، که موجب پیغام خطایی به مضمون آن که علامت نقلقول تکی در کلمه "you're" تنها(بدون جفت) است، میگردد - این ازآن نوع مواردی است که میتواند یک برنامهنویس شل را کلافه کند، بنابراین مراقب اینها باشید!
این کاربرد دستور داخلی read
شل است که از ورودی استاندارد یک سطر را میخواند، و در متغیر ارائه شده به آن، قرار میدهد .
توجه نمایید که اگر نام کامل خود را به آن بدهید، و حتی از علامتهای نقلقول دوتایی در اطراف شناسه دستور echo
نیز استفاده نکنید، هنوز هم خروجی شکل صحیحی دارد . چطور انجام شده است ؟ قبلاً برای تنظیم متغیر MY_MESSAGE
ما باید از نقلقول دوگانه در اطراف آن استفاده میکردیم!
چیزی که اتفاق میافتد، آن است که دستور read
به طور خودکار در اطراف ورودیاش نقلقولها را قرار میدهد، بنابراین با فاصلهها به طور صحیح رفتار میشود . (البته در نمونههایی همچون echo "$MY_MESSAGE"
لازم است از نقلقولها استفاده کنید).
محدوده متغیرها
در شل بورن، آنطور که درزبانهایی مثل C انجام میشود، نیازی به تعریف متغیرها نمیباشد، . اما اگر سعی کنید متغیر تعریف نشدهای را بخوانید، نتیجه یک رشته تهی است . هشدارهای خطا دریافت نمیکنید .
این مورد میتواند موجب باگهای ظریفی بشود- اگرشما واگذاری مقدار MY_OBFUSCATED_VARIABLE=Hello
را انجام داده وسپس دستور echo $MY_OSFUCATED_VARIABLE
را اجرا کنید، هیچ چیز دریافت نمیکنید, ( به عنوان دومین نکته مبهم ) .
دستوری به نام export
موجود است که در محدوده وجود متغیرها تأثیر اساسی دارد . برای اینکه به درستی بدانید با متغیرها چگونه رفتار میشود، لازم است، مواردی از چگونگی استفاده از این دستور را بفهمید .
اسکریپت کوچک myvar2.sh
را ایجاد کنید:
myvar2.sh
#!/bin/sh
echo "MYVAR is: $MYVAR"
MYVAR="hi there"
echo "MYVAR is: $MYVAR"
حالا اسکریپت را اجرا کنید:
$ ./myvar2.sh
MYVAR is:
MYVAR is: hi there
ابتدا هیچ کمیتی به MYVAR اختصاص نیافته، بنابراین تهی است. سپس مقداری به آن نسبت دادهایم، ونتیجه مورد انتظار نمایش داده میشود.
حالا به این صورت اجرا کنید:
$ MYVAR=hello
$ ./myvar2.sh
MYVAR is:
MYVAR is: hi there
هنوزهم مقدار ندارد! چه بر سرش آمده است؟!
وقتی اسکریپت myvar2.sh
را از شل محاورهای فراخوانی میکنید، یک پوسته جدید برای اجرای اسکریپت تولیدمثل میکند. این به علت وجود سطر #!/bin/sh
در ابتدای اسکریپت است، که قبلاً بحث کردیم.
برای آن که یک متغیر به برنامهدیگری از جمله اسکریپت شل به ارث برسد، لازم است که آن را export
نماییم. تایپ کنید:
$ export MYVAR
$ ./myvar2.sh
MYVAR is: hello
MYVAR is: hi there
حالا به سطر سوم اسکریپت نگاه کنید:این سطر محتوای متغیر
MYVAR
را تغییر میدهد. اما راهی برای ارسال آن به عقب، یعنی به پوسته محاورهای شما وجود ندارد. سعی کنید مقدار متغیر MYVAR
را بخوانید:
$ echo $MYVAR
hello
$
وقتی که اسکریپت پوسته خارج میشود، محیط آن هم نابود میشود. امامتغیر MYVAR
مقدار اولیه hello
در شل محاورهای شما را در خود دارد.
به منظور دریافت تغییرات محیط از اسکریپت به شل محاورهای، باید اسکربپت را
source کنیم - این دستور در عوض تولید شل جدید برای اجرای اسکریپت، آن را به طور مؤثر در پوسته محاورهای ما اجرا میکند.
میتوانیم با دستور "." نیز اسکریپت را source نماییم:
$ MYVAR=hello
$ echo $MYVAR
hello
$ . ./myvar2.sh
MYVAR is: hello
MYVAR is: hi there
$ echo $MYVAR
hi there
تغییر ایجاد شده در متغییر، این مرتبه به شل ما سرایت کرده! این چگونگی کارکرد، به عنوان مثال، فایل .profile
یا فایل .bash_profile
شما میباشد.
توجه کنید که در این حالت، اجرای export MYVAR
را لازم نداریم.
با تشکر از sway برای نشان دادن آن که، من ابتدا در کد بالا گفته بودم echo MYVAR
نه آن طور که باید میبود echo $MYVAR
.
یک مورد با ارزش دیگر در باره متغیرها و قابل ذکر در اینجا، ملاحظه نمودن اسکریپت زیر است:
#!/bin/sh
echo "What is your name?"
read USER_NAME
echo "Hello $USER_NAME"
echo "I will create you a file called $USER_NAME_file"
touch $USER_NAME_file
فکر کنید،چه نتیجهای از آن انتظار دارید . به طور نمونه اگر "steve"
را به عنوان USER_NAME، وارد کنید، اسکریپت باید فایل steve_file
را ایجاد کند؟
در واقع، خیر. این موجب یک خطای عدم وجود متغیری به نام
USER_NAME_file
میگردد. شل نمیداند کجا نام متغیر تمام میشود و باقیمانده شروع میشود.
چطور میتوانیم این را مشخص کنیم؟
پاسخ این است، که ما خودِ متغیر را در ابروها محصور میکنیم :
user.sh
#!/bin/sh
echo "What is your name?"
read USER_NAME
echo "Hello $USER_NAME"
echo "I will create you a file called ${USER_NAME}_file"
touch "${USER_NAME}_file"
اکنون پوسته میداند که ما داریم به متغیر USER_NAME
ارجاع میدهیم ومیخواهیم که پسوند _file
به محتوای متغیر متصل گردد.
چنین مواردی میتواند سقوط بسیاری از برنامهنویسان تازه وارد اسکریپت شل باشد، چرا که پیگردی سرچشمه معما میتواند دشوار باشد.
همچنین به علامتهای نقلقول در اطراف "${USER_NAME}_file"
توجه نمایید - اگر کاربر "Steve Parker" را وارد کند (با درج فاصله )، سپس شناسهها بدون نقلقولها به دستور touch
داده شوند، Steve
و Parker_file
خواهند بود - یعنی، در واقع مثل آن که گفته باشیم touch Steve Parker_file
، که دو فایل است که باید touch
بشود، نه یک فایل. نقلقولها باعث پرهیز از این امر میشوند. با تشکر از Chris به خاطر واضح نمودن این مورد.