آموزش اسکریپت نویسی لینوکس (آرایه ها)
آموزش اسکریپت نویسی لینوکس (آرایه ها)
به طوری که قبلاً اشاره شد، BASH سه نوع پارامتر ارائه میکند:
- رشتهها
- اعداد صحیح
- و آرایهها
بدون تردید رشتهها پر استفادهترین نوع پارامترها میباشند. اما آنها همچنین، بد رفتارترین نوع پارامترها هستند.
اهمیت دارد که به خاطر بسپاریم، یک رشته فقط یک عنصر را نگاه میدارد.
به عنوان نمونه، گرفتن خروجی یک فرمان، و قرار دادن آن در پارامتر رشتهای بدین معنا میباشد که پارامتر فقط یک رشته از کاراکترها میباشد،
صرفنظر از اینکه آیا آن رشته نام بیست فایل، بیست عدد، یا نام بیست نفر را نمایندگی میکند.
و همینطور است که همیشه وقتی اقلام چندگانه را در یک رشته منفرد قرار میدهید، باید این اقلام را به طریقی از یکدیگر جدا کنید.
ما به عنوان انسان معمولاً میتوانیم وقتی به یک رشته نگاه میکنیم نام فایلهای مختلف را کشف کنیم.
ما فرض میکنیم که شاید هر سطر در یک رشته نام یک فایل را نشان میدهد، یا هر کلمه نام یک فایل را نمایندگی میکند.
در حالیکه این پنداشت قابل درک است، همچنین به طور ذاتی معیوب است.
هر نام فایل منفرد میتواند شامل هر کاراکتری باشد که ممکن است شما بخواهید برای جدا کردن نام فایلها در یک رشته استفاده کنید.
به این معنی که از نظر تکنیکی گفته نمیشود که نام اولین فایل در کجای یک رشته به پایان میرسد،
زیرا کاراکتری وجود ندارد که بتواند بگوید: «من به پایان نام فایل اشاره میکنم» چون آن کاراکتر خودش میتواند بخشی از نام فایل باشد.
غالباً، اشخاص این اشتباه را مرتکب میگردند:
* این در حالت کلی کار نمیکند:
/files=$(ls ~/*.jpg); cp $files /backups $
در حالیکه احتمالاً این میتواند ایده بهتری باشد.
* این در حالت کلی کار میکند:
/files=(~/*.jpg); cp “${files[@]}” /backups $
تلاش اولی در پشتیبانگیری از فایلهای دایرکتوری جاری معیوب است.
ما خروجی دستور ls را در یک رشته به نام files قرار دادهایم
و سپس از بسط پارامتر $filesبه صورت غیر نقلقولی برای بریدن آن رشته به شناسهها (بر مبنای تفکیک کلمه) استفاده کردهایم.
به طوری که قبلا اشاره شد، تفکیک کلمه و شناسه، یک رشته را از جایی که فضای سفید وجود دارد به قطعاتی برش میدهد.
استناد به بسط فوق به این معنی است که فرض کردهایم در نام فایلهای ما هیچ فضای سفیدی نیست،
اگر باشد نام فایل به دو نیمه یا بیشتر بریده میشود.
تنها روش مطمئن نشان دادن عناصر چندگانه رشته در Bash از طریق استفاده از آرایهها میباشد.
آرایه نوعی متغیر است که رشتهها را با اعداد ترسیم میکند.
این اساسا به معنای آن است که یک لیست شماره گذاری شده از رشتهها را نگهداری میکند.
چون هر یک از این رشتهها یک هویت (عنصر) جداگانه است، میتواند بدون خطر هر کاراکتری، حتی فضای سفید را در خود داشته باشد.
برای بهترین نتیجه و کمترین دردسر، به خاطر بسپارید که اگر لیستی از عناصر دارید، همیشه باید آنها را در یک آرایه قرار دهید.
ایجاد آرایهها
چند روش موجود است که میتوانید آرایهها را ایجاد نموده یا با دادهها پر کنید.
یک روش صحیح منفرد وجود ندارد: روشی که شما نیاز خواهید داشت بستگی به آن دارد که دادهها کدامند و از کجا میآیند.
ساده ترین راه برای ایجاد یک آرایه ساده با داده، استفاده از ترکیب =() میباشد:
(“names=(“Bob” “Peter” “$USER” “Big Bad John $
این ترکیب دستوری (syntax) برای ایجاد آرایههایی با دادههای ایستا یا مجموعهای از پارامترهای رشتهای معلوم، عالی است،
اما قابلیت انعطاف بسیار کمی برای افزودن مقادیر زیاد عناصر آرایه، در اختیار میگذارد.
اگر انعطاف پذیری بیشتری میخواهید، میتوانید از شاخصهای صریح استفاده کنید:
(“names=([0]=”Bob” [1]=”Peter” [20]=”$USER” [21]=”Big Bad John $
…or #
“names[0]=”Bob $
توجه نمایید که بین شاخص 1 و 20 در این مثال یک شکاف وجود دارد.
یک آرایه با حفرههایی در آن آرایه پراکنده نامیده میشود.
Bash این امر را اجازه میدهد و اغلب میتواند کاملا سودمند باشد.
اگر میخواهید یک آرایه را با نام فایلها پر کنید، ممکن است احتمالا بخواهید از Globs استفاده کنید:
(photos=(~/”My Photos”/*.jpg $
توجه نمایید که در اینجا بخش My Photos را نقلقول کردهایم زیرا شامل یک فاصله است
اگر این کار را نمیکردیم، Bash آن را به صورت (photos=(‘~/My’ ‘Photos/’ *.jpg تفکیک مینمود،
که به وضوح آنچه ما میخواستیم نبود، همچنین توجه نمایید که ما فقط بخش شامل فاصله را نقلقولی کردیم.
به این دلیل چنین است که ما نمیتوانیم ~ یا * را نقلقولی کنیم،
اگر چنین کنیم، آنها کاراکترهای لفطی میشوند و Bash دیگر با آنها همچون کاراکترهای خاص رفتار نمیکند.
ایجاد آرایههای ابهام آمیز با یک گروه نام فایل می توانند به روش زیر ایجاد شوند:
!files=$(ls) # BAD, BAD, BAD
!files=($(ls)) # STILL BAD
به یاد داشته باشید همیشه از کاربرد ls به این شکل پرهیز کنید، اولی یک رشته با خروجی فرمان ls ایجاد میکند.
آن رشته احتمالاً به دلیلی که در مقدمه آرایهها اشاره شد نمیتواند به طور بی خطر به کار برود.
دومی نزدیکتر است، اما هنوز نام فایلها را با فضای سفید تفکیک میکند.
روش صحیح انجام آن این است:
(*)=files $
این جمله یک آرایه به ما میدهد که در آن هر نام فایل یک عنصر جداگانه است.
گاهی اوقات میخواهیم یک آرایه از یک رشته یا خروجی یک فرمان تشکیل بدهیم.
خروجی فرمان ها رشته هستند.
برای نمونه، اجرای یک فرمان find نام فایلها را به شمار میآورد
و آنها را با یک کاراکتر سطر جدید (قرار دادن هر نام فایل در یک سطر جداگانه) از هم جدا میکند.
بنابراین برای تفکیک یک رشته بزرگ به داخل یک آرایه، لازم است به Bash بگوییم هر عضو کجا به انتها میرسد.
آنچه برای شکستن یک رشته به کار میرود محتوای متغیر IFS میباشد:
“IFS=. read -a ip_elements <<< “127.0.0.1
در اینجا از متغیر IFS با محتوای . برای بریدن آدرس IP داده شده به عناصر آرایه از جایی که . وجود دارد، نتیجه یک آرایه با عناصر، 127 و 0 و 0 و 1 است.
میتوانستیم همین کار را با دستور find انجام بدهیم، در صورتی که متغیر IFS را به کاراکتر سطر جدید تنظیم میکردیم.
اما موقعی که شخصی فایلی دارای کاراکتر سطر جدید ایجاد نماید (به طور اتفاقی یا بدخواهانه)، اسکریپت ما کار نخواهد کرد.
بنابراین، آیا روشی برای دریافت لیستی از عناصر از یک برنامه خارجی (مانند find) در یک آرایه Bash وجود دارد؟
به طور کلی، پاسخ بلی است، به شرط آنکه راه قابل اطمینانی برای جداسازی عناصر موجود باشد.
در یک حالت خاص از نام فایل ها، پاسخ این مشکل، بایتهای تهی (NUL) است.
یک بایت تهی، بایتی است که همه بیتهای آن صفر است. (00000000)
رشتههای Bash نمیتوانند شامل بایتهای تهی باشند،
اما به عنوان یک محصول زبان برنامهنویسی “C”، به دلیل اینکه در زبان C بایت تهی برای علامت گذاری انتهای رشته به کار رفته است.
از این جهت Bash که به زبان C نوشته شده و از رشتههای بومی C استفاده میکند، این رفتار را به ارث میبرد.
یک جریان داده (مانند خروجی یک فرمان، یا یک فایل) میتواند شامل بایت تهی باشد.
جریانها مانند رشتهها هستند، با سه تفاوت عمده:
- آنها به صورت ترتیبی خوانده میشوند،
- آنها یک سویه میباشند (شما میتوانید از آنها بخوانید یا در آنها بنویسید، اما به طور نوعی هر دو با هم میسر نیست)،
- و آنها میتوانند شامل بایتهای تهی باشند.
نه نامهای فایل میتوانند شامل بایت تهی باشند (چون آنها توسط یونیکس همانند رشتههای C تکمیل شدهاند )،
و نه اکثریت وسیع اقلام قابل خواندن برای انسان که شاید ما بخواهیم در یک اسکریپت ذخیره کنیم (از قبیل نام افراد، آدرسهای IP، و غیره ).
این موضوع NUL را یک نامزد عالی برای جداسازی عناصر در یک جریان، میسازد.
به طور کلی دستوری که میخواهید خروجی آن را بخوانید، یک گزینهای خواهد داشت، که خروجیاش را به صورت جدا شده با بایت تهی، به جای سطر جدید یا کاراکتر دیگری، ایجاد میکند.
دستور find (در GNU و BSD) گزینه -print0را دارد، که ما در این مثال استفاده خواهیم نمود:
()=files
while read -r -d $’\0′; do
(“files+=(“$REPLY
(done < <(find /foo -print0
این یک روش مطمئن تفکیک خروجی یک فرمان به رشتهها میباشد.
به طور قابل فهمی، ابتدا کمی به هم پیچیده و گیج کننده به نظر میرسد.
لذا، بیایید کمی آن را باز کنیم:
سطر اول files=() یک آرایه خالی به نام files ایجاد میکند.
ما از یک حلقه while استفاده میکنیم که هر مرتبه یک دستور read را اجرا میکند.
فرمان read از گزینه ‘ -d $’\ 0استفاده میکند،
به آن معنا که به جای خواندن یک سطر در هر دفعه (تا رسیدن به یک کاراکتر سطر جدید)، تا رسیدن به بایت NUL میخوانیم .(\0)
همچنین از گزینه -rبرای جلوگیری از رفتار ویژه با کاراکتر \ استفاده میکند.
وقتی read مقداری از داده ها را میخواند و با یک بایت تهی مواجه میشود،
بدنه حلقه while اجرا میگردد. ما آنچه را خواندهایم (که در متغیر REPLY قرار دارد) در آرایه قرار میدهیم.
برای انجام این کار، ما از ترکیب +=() استفاده میکنیم.
این ترکیب دستوری یک یا چند عنصر را به انتهای آرایه ما اضافه میکند.
و سرانجام، ترکیب دستوری < <(..) که ترکیبی از یک تغیر مسیر فایل (<)و جایگزینی پردازش (<(..)) میباشد.
در حال حاضر صرف نظر از جزئیات تکنیکی، به سادگی میگوییم این چگونگی ارسال خروجی فرمان find به درون حلقه while ما میباشد.
همان طور که قبلاً بیان گردید، فرمان find خود با یک گزینه -print0به کار رفته، که به او بگوید، نام فایل هایی که مییابد را، با یک بایت تهی تفکیک کند.
جهت مشاهده دوره های آموزشی بر روی این لینک کلیک نمایید.
جدیدترین اخبار مجموعه فراز نتورک را در این صفحه اجتماعی دنبال کنید.
نویسنده: موسی رشوند
دیدگاهتان را بنویسید
برای نوشتن دیدگاه باید وارد بشوید.