14. نکتهها و اشارهها
یونیکس سرشار از برنامههای کاربردی کار با متن میباشد، برخی ازابزارهای قدرتمندتر را اکنون در این بخش از آموزش بحث خواهیم نمود. اهمیت آن در این است که به طور ضمنی هر چیزی در یونیکس متناست . به طور ضمنی هر چیزی که بتوانید به آن فکر کنید یا به وسیله یک فایل متنی دیگر، یا بوسیله رابط خط فرمان (CLI) کنترل میشود. تنها چیزی که نمیتوانید با یک شل اسکریپت خودکار کنید، یک برنامه یا ویژگی فقط GUIمیباشد. تحت یونیکس موارد خیلی زیادی از اینها وجود ندارد!
ممکن است شنیده باشید که گفته شده در *nix، "هر چیز یک فایل است" - این درست است.
ما در این جا چند زیرفهرست داریم ... که مقصود اصلی، نکتهها و اشارهها را دنبال میکنند.
کاربرد trap دانستن آن که چهوقت یک سیگنال توقف از قبیل CTRL-C و غیره دریافت کردهاید.
چارهجویی برای دوگانگی 'echo -n' و 'echo \c'
نمونه مستند سازی شده از اسکریپت زندگی واقعی، که من نوشتم - به کاربران کمک میکند یک مودم SpeedTouch را پیکربندی کنند،و در آدرس http://speedtouchconf.sourceforge.net/ در دسترس میباشد.
ما قبلاً یک استفاده از فرمان ساده اما قدرتمند cut
را نشان دادهایم.
در این جا چند مثال از بعضی برنامههای خارجی رایجتر که استفاده میشوند، را بحث خواهیم نمود.
grep
یک برنامه کاربردی به نهایت سودمند برای برنامهنویس پوسته میباشد.
یک مثال از grep چنین خواهد بود:
#!/bin/sh steves=`grep -i steve /etc/passwd | cut -d: -f1` echo "All users with the word \"steve\" in their passwd" echo "Entries are: $steves"
اگر این اسکریپت فقط یک مورد انطباق داشته باشد، عالی است. به هرحال اگر دو سطر با کلمه "steve" در آنها، در فایل /etc/passwd وجود داشته باشد، سپس پوسته محاورهای اینطور نمایش خواهد داد:
$> grep -i steve /etc/passwd steve:x:5062:509:Steve Parker:/home/steve:/bin/bash fred:x:5068:512:Fred Stevens:/home/fred:/bin/bash $> grep -i steve /etc/passwd |cut -d: -f1 steve fred
اما در اسکریپت ما چنین خواهد بود:
Entries are: steve fred
با قرار دادن نتیجه در یک متغیر ما سطر جدید را به فاصله تغییر داده ایم،
manpage(صفحه راهنمای) sh
به ما میگویدکه اولین کاراکتر در متغیر $IFS
برای این منظور استفاده خواهد شد. IFS به طور پیشفرض <space><tab><cr> می باشد.
شاید اگر به هرحال میخواستیم کاراکتر سطر جدید را نگهداریم:
میتوانست بهتر به نظر آید که فاصله را به سطرجدید تبدیل کنیم....این کاری برای tr
است:
#!/bin/sh steves=`grep -i steve /etc/passwd | cut -d: -f1` echo "All users with the word \"steve\" in their passwd" echo "Entries are: " echo "$steves" | tr ' ' '\012'
توجه کنید که tr
فاصله را به کاراکتر اکتال 012 (NEWLINE) تبدیل نموده.
کاربرد رایج دیگر برنامه tr
استفاده آن از دامنه است... این برنامه میتواند حالت حروف متن را به بزرگ یا کوچک تبدیل نماید، به عنوان مثال:
#!/bin/sh steves=`grep -i steve /etc/passwd | cut -d: -f1` echo "All users with the word \"steve\" in their passwd" echo "Entries are: " echo "$steves" | tr ' ' '\012' | tr '[a-z]' '[A-Z]'
در اینجا یک ترجمه از [a-z] به [A-Z] اضافه کردهایم. توجه نمایید که دقیقاًهمان تعداد کمیت دردامنه a-z وجود دارد که در A-Z. سپس این میتواند هر کاراکتر قرار گرفته در دامنه اسکی a-z را به A-Z ترجمه کند ... به بیان دیگر، حروف را از حالت کوچک به حالت بزرگ تبدیل کند. tr
در حقیقت باهوشتر از این است: tr [:lower:] [:upper:]
همان کار را به خوبی انجام میدهد، واحتمالاًخواناتر. همچنین این برنامه قابل حمل نیست هر tr
نمیتواند این را انجام دهد.
حقهزدن
آنها که نمیتوانند ... حقه بزنند
در حقهزدن چیز اشتباهی نیست! در پوسته بعضی موارد خیلی خوب نیستند. دو ابزار مفید sed
و awk
میباشند.
در حالیکه اینها برنامههای بزرگ قدرتمندی هستند، که در جای خودشان میتوانند به عنوان یک زبان برنامهنویسی کوچک به کار روند ، آنها اغلب به دلایل بسیار ساده و مخصوصی در اسکریپت پوسته به کار میروند.
ضمن اینکه این به آن معناست که سیستم باید برنامههای اجرایی بزرگی( (52k برای
sed
و 110k برای awk
) را بارگذاری کند، که انجام آن نامطبوع است، دلیل این که یک استادکار خوب ابزارش را سرزنش نمیکند آن است که، استادکارخوب در وهله اول از ابزارهای درست استفاده میکند.
بنابراین به من اجازه دهید این دو را با کاربردهای خیلی ساده معرفی کنم.
حقهزدن با awk
wc
، را که تعداد کاراکترها، سطرها، و کلمه ها در یک فایل متن را میشمارد، ملاحظه کنید. خروجیاش چنین است:
$ wc hex2env.c 102 189 2306 hex2env.c
اگر میخواستیم تعداد سطرها را در متغیر قرار دهیم، به سادگی این کد را به کار میبردیم:
NO_LINES=`wc -l file`
که باید تمام سطر رابخواند.
چون خروجی پر از فاصله است، نمیتوانیم به طور امکانپذیری عدد 102
را در یک رشته به دست آوریم. درعوض، از این واقعیت استفاده میکنیم که awk
به طوری مشابه با scanf
در C کارمیکند - وفضاهای سفید ناخواسته را حذف میکند. و $1 $2 $3
وغیره را در متغیرها قرار میدهد. بنابراین ما از این ساختار استفاده میکنیم:
NO_LINES=`wc -l file | awk '{ print $1 }'`
حالا متغیر NO_LINES برابر 102 است.
حقهزدن با sed
برنامه سودمند دیگر sed است - ویرایشگر جریانی-. پرل در کار کردن با عبارتهای منظم خیلی خوب است، پوسته اینطور نیست. بنابراین به سرعت میتوانیم ساختار s/from/to/g
را با فراخوانی sed
به کار ببریم. به طور نمونه:
sed s/eth0/eth1/g file1 > file2
تمام نمونههای eth0
در file1 به نمونههای eth1
تغییر داده و نتیجه را در file2 قرار میدهد
اگر میخواستیم فقط یک کاراکترمنفرد را تغییر بدهیم، tr
ابزاری بود که استفاده میشد، کوچکتر بودن و بنابراین سریعتر برای بارگذاری .
مورد دیگر اینکه tr
نمی تواند کاراکترها را از یک فایل حذف کند:
echo ${SOMETHING} | sed s/"bad word"//g
این کد، عبارت "bad word" را از متغیر ${SOMETHING}
حذف میکند.
ممکن است وسوسه شوید، که بگویید ، "اما grep
میتواند آن را انجام دهد" - grep فقط با تمامیت سطرها کار میکند. فایل را ملاحظه کنید:
This line is okay. This line contains a bad word. Treat with care. This line is fine, too.
Grep دومین سطر را به طور کامل حذف میکند و فقط یک فایل دوسطری به جا میگذارد، sed فایل را برای خواندن تغییر میدهد:
This line is okay. This line contains a . Treat with care. This line is fine, too.
اشاره Telnet
این یک تکنیک مفید است که من از برنامه مرورگر Sun بیرون کشیدم. اگرچه مدت زیادی است که telnet در سرویس دهندهها استفاده نمیشود، هنوز توسط برخی دستگاههای شبکه به کار میرود، از قبیل متمرکز کننده ترمینال و مشابه آن. به وسیله یک اسکریپت مانند این، اسکریپت خودتان، یا از خط فرمان، میتوانید اجرا کنید:
$ ./telnet1.sh | telnet
افرادی در این باره از من پرسیدهاند، و مایل بودهاند آنها را به طرف مجموعه کد مورد انتظار اشاره دهم، که تا حدودی پیچیده و حجیم است، این کد باید تا حدودی در میان سیستمها قابل حمل باشد(مادامی که آنها egrep را دارند ). اگر در سیستم شما کار نمیکند، استفاده از grep گنو با گزینه -q را امتحان کنید، یا یک grep اختصاصی و هدایت به /dev/null، بههرحال هنوز از نصب expect به مقدار زیادی آسانتر است.
telnet1.sh
#!/bin/sh host=127.0.0.1 port=23 login=steve passwd=hellothere cmd="ls /tmp" echo open ${host} ${port} sleep 1 echo ${login} sleep 1 echo ${passwd} sleep 1 echo ${cmd} sleep 1 echo exit
به هرحال Sun مقداری کد کنترل خطای هوشمند اضافه کرد( توجه داشته باشید که متغیرهایی که میتوانید از پوسته جاری یا اسکریپت پوسته تنظیم و صادر کنید، نگهداری کلمه عبور در فایلهای قابل خواندن را موقوف کنند ):
$ ./telnet2.sh | telnet > file1
telnet2.sh
#!/bin/sh # telnet2.sh | telnet > FILE1 host=127.0.0.1 port=23 login=steve passwd=hellothere cmd="ls /tmp" timeout=3 file=file1 prompt="$" echo open ${host} ${port} sleep 1 tout=${timeout} while [ "${tout}" -ge 0 ] do if tail -1 "${file}" 2>/dev/null | egrep -e "login:" > /dev/null then echo "${login}" sleep 1 tout=-5 continue else sleep 1 tout=`expr ${tout} - 1` fi done if [ "${tout}" -ne "-5" ]; then exit 1 fi tout=${timeout} while [ "${tout}" -ge 0 ] do if tail -1 "${file}" 2>/dev/null | egrep -e "Password:" > /dev/null then echo "${passwd}" sleep 1 tout=-5 continue else if tail -1 "${file}" 2>/dev/null | egrep -e "${prompt}" > /dev/null then tout=-5 else sleep 1 tout=`expr ${tout} - 1` fi fi done if [ "${tout}" -ne "-5" ]; then exit 1 fi > ${file} echo ${cmd} sleep 1 echo exit
توجه کنید با این نگارش، خروجی در فایل file1
قاپیده میشود، و آن فایلی است که در واقع به وسیله اسکریپت برای کنترل پیشرفتش استفاده میشود. من عبارت "> ${file}"
را اضافه کردهام به طوری که خروجی رسیده به داخل فایل فقط خروجی فرمان است، و فرایند اتصال همراه آن نیست.