Script ها کدی که مینویسیم تا پشت سر هم اجرا بشود (مثل فایلهای Bat. و Cmd. در MS Win/DOS )
test.sh:
echo "Hello World!"
echo "Salam be jahan!"
برای اجرا باید دستوری مثل sh test.sh بنویسیم اگه بخوایم که فایل text تبدیل به فایل اجرایی بشه در اول فایل خط
#!/bin/sh
رو وارد کنیم و فایل رو با دستور
chmod +x test.sh
به فایل اجرایی تبدیل کنیم بعد از اون میتونیم فایل رو مثل فایلهای اجرایی دیگه اجرا کنیم:
./test.sh
نوشتن comment در برنامه
با گذاشتن علامت # در ابتدای هر خط ، آن خط از برنامه comment می شود البته !# یک حالت استثناست .
پایان خط در shell
- اول بگم که هر جا خواستیم به خط جاری پایان بدیم و ادامهی اون رو توی خط بعدی بنویسیم از \ استفاده میکنیم:
echo \
"Salam"
- دوم هر کجا که به خط بعد رفتیم معادل ; حساب میشه و بلعکس. این دو قسمت معادلند:
if true
then
echo "Condition is true"
fi
if true; then echo "Condition is true"; fi
متغییرها
متغییرها به صورت name=value اختصاص داده میشوند و با name$ فراخوانی میشوند.
myname="Arman"
echo "My name is $myname"
دستور read
با دستور read میتونیم از ورودی متغییرها رو بخونیم
read varname
بلوک if
ساختار if به صورت زیر است:
if condition
then commands
[elif condition
then commands]
[else commands]
fi
به جای condition شرط را وارد میکنیم و جای commands دستورات رو. قسمتهای elif و else اختیاری هستند برای نوشتن conditionهای مختلف از برنامهی test استفاده میکنیم به دو صورت میتونیم از test استفاده کنیم:
- test condition
- [condition ]
مثال:
read name
if [ $name = "Arman" ]
then
echo "Your name is Arman"
elif [ $name = "Mehdi" ] # ;))
then
echo "Your name is Mehdi"
else
echo "I don’t know you"
fi
Shell شرط ورودی رو اجرا میکنه و اگر مقدار صفر رو برگردونند دستورات رو اجرا میکنه و اگر غیر صفر بود اجرا نمیکنه (یا else رو اجرا میکنه)
بهتره اینجوری بگم که برعکس زبانهایی مانند خانوادهی C که صفر برابر False (غلط) و غیر صفر برابر True (درست) هست توی shell صفر برابر True و غیر صفر برابر False هست
پارامترهای مختلف test
دستور test پارامترهای زیادی داره بعضی از اونها رو نوشتم: exp = عبارت str = رشته int = عدد صحیح file = اسم فایل
!exp اگر exp غلط باشد (not)
exp1 -a exp2 اگر exp1 و exp2 هردو درست باشند (and)
exp1 -o exp2 اگر exp1 یا exp2 یا هردو درست باشند (or)
str اگر طول str ناصفر باشد
str1 = str2 اگر str1 برابر str2 باشد
str1 != str2 اگر str1 مخالف str2 باشد
int1 -eq int2 اگر int1 برابر int2 باشد
int1 -nq int2 اگر int1 مخالف int2 باشد
int1 -gt int2 اگر int1 بزرگتر از int2 باشد
int1 -ge int2 اگر int1 بزرگتر یا مساوی int2 باشد
int1 -lt int2 اگر int1 کوچکتر از int2 باشد
int1 -le int2 اگر int1 کوچکتر یا مساوی int2 باشد
-e file اگر file وجود داشته یاشد
-d file اگر file یک دیرکتوری باشد
-f file اگر file وجود داشته باشد و یک فایل معمولی باشد
برای اطلاعات بیشتر به man test مراجعه کنید
حلقهها
بلوک while
while condition
do commands
done
تا وقتی که condition درست باشد (کد خروجی از اجرا condition صفر باشد) commands اجرا میشوند
scrip هر ثانیه یک x رو صفحه میگذارد و وقتی تعداد آنها به ۵ رسید حلقهی while به پایان میرسه
a=
while [ "$a" != "....." ]
do
a="$a."
echo x
sleep 1
done
- به جای while ! condition میتوانیم از until condition استفاده کنیم
a=
until [ "$a" = "....." ]
do
a="$a."
echo x
sleep 1
done
بلوک for
for name in list
do commands
done
به ازای هر بار اجرای command متغییر name یکی از مقادیر list رو به خود میگیره
این script اعداد ۱ تا ۶ رو مینویسه
for number in 1 2 3 4 5 6
do
echo -n "$number "
done
echo
این یکی اسم تمام فایلهای دیرکتوری جاری رو مینویسه
for fn in *
do
echo $fn
done
دستورهای break و continue
با دستور continue دستورهای بعدی حلقه استفاده اجرا نمیشود و دوباره به اول حلقه میرود با دستور break دستورهای بعدی حلقه استفاده اجرا نمیشود و برنامه از حلقه خارج میشود
اگر حلقههای تو در تو داریم و میخواهیم دو یا چند بار break یا continue کنیم جلوی این دستورات تعداد را وارد میکنیم
این script تمام اعداد دو رقمی قبل از ۷۳ را مینویسد
for a in 0 1 2 3 4 5 6 7 8 9
do
for b in 0 1 2 3 4 5 6 7 8 9
do
echo "$a$b"
if [ "$a$b" = "72" ]
then
break 2
fi
done
done
بلوک case
case name in
match1) commands ;;
match2) commands ;;
esac
اگر میخواهیم مقدارهای مختلف یک متغییر را بررسی کنیم میتوانیم به جای چندین if از case استفاده کنیم در صورتی که میخواهیم چند مقدار را بررسی کنیم (با یک عبارت) آنها را با | از هم دیگر جدا میکنیم مثلا
name|NAME)
commands
;;
مثال
echo -n "Enter your command: "
read cmd
case $cmd in
listFiles)
ls
;;
showDirectory)
pwd
;;
calender)
date
echo
cal
;;
*)
echo "OOoops commands are: listFiles, showDirectory, calender"
;;
esac
|| و &&
command1 && command2
این دستور command1 را اجرا میکند و اگر خروجی صفر بود (درست) command2 اجرا میشود
[ -f fileName ] && ./fileName
این قطعه کد (که خیلی استفاده میشه) یعنی اگر فایل fileName وجود داشت آنرا اجرا کن در غیر این صورت کاری انجام نمیدهد
- اگر بخواهیم چند کار انجام دهیم آنها رو با {} احاطه میکنیم
|| برعکس && هست یعنی اگر خروجی command1 غیر صفر بود command2 اجرا میشود
Redirecting
توی shell سه نوع ورودی/خروجی داریم:
- - ورودی از keyboard اسم:standard input
- - خروجی به console اسم:standard output
- - خروجی به console برای خطاها اسم: standard error
- برای توضیح بگم که stderr با stdout هیچ فرقی نداره و فقط برای جداسازی این دوتا هست
[n] > file
خروجی استاندارد یا شماره n را در file میریزد
[n]>> file
خروجی استاندارد یا شماره n را در انتهای file میریزد و آنرا پاک نمیکند
[n]< file
ورودی استاندارد یا شماره n را از file میخواند
[n]<> file
فایل file را برای ورودی و خروجی استاندارد یا n باز میکند
[n1]>&n2
خروجی استاندارد یا شماره n1 را به n2 میفرستد
[n1]<&n2
ورودی استاندارد یا شماره n1 را به n2 میفرستد
[n]<&-
ورودی استاندارد یا n را میبندد
[n]>&-
خروجی استاندارد یا n را میبندد
[n]<< endWord
write here
and here
endWord
هرچه بین دو تا endWord نوشته شده باشه رو به ورودی استاندارد یا n میفرستد
اگر میخواهید از دستور cmd هیچ خروجی روی تصویر نشان داده نشود:
cmd 2>&1 >/dev/null
* /dev/null مثل یک سطل آشغال هست هرچی توش بریزین به هیچ جایی نمیره
پایپ
فرض کنیم میخواهیم اسم فایلهای یک دیرکتوری را با ls بگیریم و با sort مرتب کنیم
ls > temp
sort < temp
به جای استفاده از فایل واسطه میتوانیم مستقیما از خروجی یک دستور خوانده و به ورودی دیگری بدیم با یه پایپ (لوله)!
ls | sort
توابع و پارامترها
توی shell توابع یک نوع برنامه هستند و بلعکس! برای همین همهی مطالب این قسمت (به جز تعریف تابع) برای یک script بدون تابع هم (که خودش یک برنامه است) برقرار است
تعریف تابع
FunctionName()
{
commands
.
.
.
return exitStatus
}
exitStatus یک عدد صحیح است که به عنوان خروجی در نظر گرفته میشود
صدازدن تابع
از اون جایی تابع همون برنامهس از همون syntax استفاده میکنیم
func param1 param2
و کد خروجی هم (از اونجایی که یه برنامه هست ) توی متغییر ?$ ذخیره میشه
- برای کسانی که نمیدونن هم بگم هر برنامه یه کد خروجی داره که توی ? ذخیره میشه که معمولا نشان دهندهی اینه که برنامه درست اجرا شده یا نه:
arman:~$ ls /
bin cdrom etc initrd lib media opt root srv tmp var
boot dev home initrd.img lost+found mnt proc sbin sys usr vmlinuz
arman:~$ echo $?
0
arman:~$ ls /err
ls: /err: No such file or directory
arman:~$ echo $?
2
برای اینکه کد خروج کل shell script رو معین کنیم یا از کل برنامه خارج شویم از تابع exit استفاده میکنیم
مثلا
exit 2
پارامترها
پارامتر اول توی 1$، پارامتر دوم توی 2$، ... و پارامتر صفرم توی 0$ ذخیره میشه
test.sh:
#!/bin/sh
echo "0 = $0 1 = $1 2 = $2"
arman:~$ ./test.sh hello world
0 = ./test.sh 1 = hello 2 = world
پارامترهای خاص
- پارامتر صفرم (0$) همون اسم برنامه هست (به همون طریقی که اجرا شده)
- متغییر #$ تعداد پارامترها را مشخص میکنه
- متغییر @$ شامل تمام متغییرها است که وقتی به string تبدیل میشود پارامترها را جداگانه مشخص میکند
- متغییر *$ شامل تمام متغییرها است که وقتی به string تبدیل میشود پارامترها به هم میچسبند و بین آنها اولین کاراکتر متغییر IFS هست (که معمولا space هست)
- متغییر $$ شماره پروسه shell جاری هست
- متغییر !$ شماره پروسه آخرین دستور background یا آخرین پایپ است
ابن مثال خیلی واضح تر از بالاست
test.sh:
#!/bin/sh
echo "0 = $0"
echo "# = $#"
echo "@ = $@"
for a in "$@"; do
echo $a
done
IFS="/"
echo "* = $*"
for a in "$*"; do
echo $a
done
echo "\$ = $$"
arman:~$ ./test.sh "hello world" hello2 world2
0 = ./test.sh
# = 3
@ = hello world hello2 world2
hello world
hello2
world2
* = hello world/hello2/world2
hello world hello2 world2
$ = 19750