منبع اصلی نوشتار زیر در این لینک قرار دارد

ایجاد آرایه ها

ادامه یادداشت قبل


راهنمای آموزشی  BashGuide   مؤلف  Lhunath

ایجاد آرایه‌ها

چند روش موجود است که می‌توانید آرایه‌ها را ایجاد نموده یا با داده‌ها پر کنید. یک روش صحیح منفرد وجود ندارد: روشی که شما نیاز خواهید داشت بستگی به آن دارد که داده‌ها کدامند و از کجا می‌آیند.

ساده‌ترین راه برای ایجاد یک آرایه ساده با داده، استفاده از ترکیب ‎=()‎ می‌باشد:

    $ 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=(*)      # Good!

این جمله یک آرایه به ما می‌دهد که در آن هر نام فایل یک عنصر جداگانه است. کامل!

این بخش که در اینجا مطرح می‌کنیم شامل برخی مفاهیم پیشرفته است. اگر هنوز آماده نیستید، شاید بخواهید پس از اینکه تمام این راهنما را خواندید به اینجا بازگردید. اگر می‌خواهید با موارد ساده ادامه دهید، می‌توانید بااستفاده از آرایه‌ها به پیش بروید.

گاهی اوقات می‌خواهیم یک آرایه از یک رشته یا خروجی یک فرمان تشکیل بدهیم. خروجی فرمانها رشته هستند: برای نمونه، اجرای یک فرمان find نام فایل‌ها را به شمار می‌آورد و آنها را با یک کاراکتر سطر جدید(قرار دادن هر نام فایل در یک سطر جداگانه) از هم جدا می‌کند. بنابراین برای تفکیک یک رشته بزرگ به داخل یک آرایه، لازم است به Bash بگوییم هر عضو کجا به انتها می‌رسد. (تذکر، این یک مثال بد است، چون نام فایل می‌تواند شامل یک سطر جدید باشد،بنابراین جدا کردن آنها با سطر جدید نمی‌تواند ایمن باشد! اما مثال زیر را نگاه کنید.)

آنچه برای شکستن یک رشته به کار می‌رود محتوای متغیر IFS می‌باشد:

    $ IFS=. read -a ip_elements ‎<<<‎ \"127.0.0.1\"

در اینجا از متغیر IFS با محتوای . برای بریدن آدرس IP داده شده به عناصر آرایه از جایی که . وجود دارد، نتیجه یک آرایه با عناصر، 127 و 0 و 0 و 1 است.

(دستور داخلی read و عملگر ‎<<<‎ به طورمفصل‌تری درفصل ورودی و خروجی. پوشش داده می‌شود)

می‌توانستیم همین کار را با دستور 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‎ به کار رفته، که به او بگوید،نام فایلهایی که می‌یابد را، با یک بایت تهی تفکیک کند.


تکرار مفید:
آرایه‌ها یک لیست مطمئن از رشته‌ها هستند. آنها برای نگهداری چندین نام فایل، بدون عیب می‌باشند.
اگر شما باید یک جریان داده را به اجزاء تشکیل دهنده عناصر تفکیک نمایید، باید طریقه‌ای برای گفتن آنکه هر عنصر از کجا شروع و به کجا ختم می‌گردد، وجود داشته باشد. بسیاری اوقات، بایت تهی بهترین انتخاب برای این کار می‌باشد.
اگر لیستی از اقلام دارید، تا آنجا که ممکن است آن را به صورت یک لیست حفظ کنید. تا موقعی که واقعاً لازم نیست، آن را در یک رشته یا فایل تخریب نکنید. اگر باید به تفصیل آن را در یک فایل بنویسید و بعداً آنرا بخوانید، مشکل جداکننده را که در بالاتوضیح داده شد، به خاطر داشته باشید.


  • در مستندات گنو: Arrays



ادامه دارد ....



به سیاره لینوکس امتیاز دهید

به اين صفحه امتياز دهيد