به طوری که قبلاً اشاره شد، BASH سه نوع پارامتر ارائه میکند: رشتهها، اعداد صحیح، و آرایهها.
بدون تردید رشتهها پر استفادهترین نوع پارامترها میباشند. اما آنها همچنین، بد رفتارترین نوع پارامترها هستند. اهمیت دارد که به خاطر بسپاریم، یک رشته فقط یک عنصر را نگاه میدارد. به عنوان نمونه، گرفتن خروجی یک فرمان، و قرار دادن آن در پارامتر رشتهای بدین معنا میباشد که پارامتر فقط یک رشته از کاراکترها میباشد، صرفنظر از اینکه آیا آن رشته نام بیست فایل، بیست عدد، یا نام بیست نفر را نمایندگی میکند.
و همینطور است که همیشه وقتی اقلام چندگانه را در یک رشته منفرد قرار میدهید، باید این اقلام را به طریقی از یکدیگر جدا کنید. ما، به عنوان انسان معمولاً میتوانیم وقتی به یک رشته نگاه میکنیم نام فایلهای مختلف را کشف کنیم. ما فرض میکنیم که، شاید، هر سطر در یک رشته نام یک فایل را نشان میدهد، یا هر کلمه نام یک فایل را نمایندگی میکند. در حالیکه این پنداشت قابل درک است، همچنین به طور ذاتی معیوب است. هر نام فایل منفرد میتواند شامل هر کاراکتری باشد که ممکن است شما بخواهید برای جداکردن نام فایلها در یک رشته استفاده کنید. به این معنی که از نظر تکنیکی گفته نمیشود که نام اولین فایل در کجای یک رشته به پایان میرسد، زیراکاراکتری وجود ندارد که بتواند بگوید: «من به پایان نام فایل اشاره میکنم» چون آن کاراکتر خودش میتواند بخشی از نام فایل باشد.
غالباً، اشخاص این اشتباه را مرتکب میگردند:
# این در حالت کلی کار نمیکند $files =$(ls ~/*.jpg); cp $ files /backups/
در حالیکه احتمالاً این میتواند ایده بهتری باشد( استفاده از آرایهها که در بخش بعد شرح داده میشوند):
# این در حالت کلی کار میکند $files =(~/* .jpg); cp "$ " /backups/{ files [@]}
تلاش اولی در پشتیبانگیری از فایلهای دایرکتوری جاری معیوب است. ما خروجی دستور ls را در یک رشته به نام
تنها روش مطمئن نشان دادن عناصر چندگانه رشته در Bash از طریق استفاده از آرایهها میباشد. آرایه نوعی متغیر است که رشتهها را با اعداد ترسیم میکند. این اساساً به معنای آن است که یک لیست شماره گذاری شده از رشتهها را نگهداری میکند. چون هر یک از این رشتهها یک هویت(عنصر) جداگانه است، میتواند بدون خطر هر کاراکتری، حتی فضای سفید را در خود داشته باشد.
برای بهترین نتیجه و کمترین دردسر، به خاطر بسپارید که اگر لیستی ازعناصر دارید، همیشه باید آنها را در یک آرایه قرار دهید.
بر خلاف برخی زبانهای برنامهنویسی، Bash لیستها، رکوردها و غیره را ارائه نکرده است. فقط آرایهها و آرایههای انجمنی( که در نگارش 4 از Bash جدید است).
آرایهها: یک آرایه لیست شماره گذاری شده رشتهها است: رشتهها را با اعداد صحیح مرتبط میکند .
چند روش موجود است که میتوانید آرایهها را ایجاد نموده یا با دادهها پر کنید. یک روش صحیح منفرد وجود ندارد: روشی که شما نیاز خواهید داشت بستگی به آن دارد که دادهها کدامند و از کجا میآیند.
سادهترین راه برای ایجاد یک آرایه ساده با داده، استفاده از ترکیب
$names =( "Bob" "Peter" "$USER" "Big Bad John" )
این ترکیب دستوری(syntax) برای ایجاد آرایههایی با دادههای ایستا یا مجموعهای از پارامترهای رشتهای معلوم، عالی است، اما قابلیت انعطاف بسیار کمی برای افزودن مقادیر زیاد عناصر آرایه، در اختیار میگذارد. اگر انعطاف پذیری بیشتری میخواهید، میتوانید از شاخصهای صریح استفاده کنید:
$names =( # or... $[0] ="Bob"[1] ="Peter"[20] ="$USER"[21] ="Big Bad John")names [0] = "Bob"
توجه نمایید که بین شاخص 1و 20 در این مثال یک شکاف وجود دارد. یک آرایه باحفرههایی در آن آرایه پراکنده نامیده میشود. Bash این امر را اجازه میدهد واغلب میتواند کاملاً سودمند باشد.
اگر میخواهید یک آرایه را با نام فایلها پر کنید، ممکن است احتمالاً بخواهید از Globs استفاده کنید:
$photos =(~/"My Photos"/*.jpg)
توجه نمایید که در اینجا بخش
متأسفانه، به راستی ایجاد آرایههای ابهام آمیز با یک گروه نام فایل که به روش زیر ایجاد میشوند، آسان است:
$files =$(ls) # BAD, BAD, BAD! $files =($(ls)) # STILL BAD!
به یاد داشته باشید همیشه از کاربرد ls به این شکل پرهیز کنید، اولی یک رشته با خروجی فرمان ls ایجاد میکند. آن رشته احتمالاً به دلیلی که در مقدمه
روش صحیح انجام آن این است:
$files =(*) # Good!
این جمله یک آرایه به ما میدهد که در آن هر نام فایل یک عنصر جداگانه است. کامل!
این بخش که در اینجا مطرح میکنیم شامل برخی مفاهیم پیشرفته است. اگر هنوز آماده نیستید، شاید بخواهید پس از اینکه تمام این راهنما را خواندید به اینجا بازگردید. اگر میخواهید با موارد ساده ادامه دهید، میتوانید بااستفاده از آرایهها به پیش بروید.
گاهی اوقات میخواهیم یک آرایه از یک رشته یا خروجی یک فرمان تشکیل بدهیم. خروجی فرمانها رشته هستند: برای نمونه، اجرای یک فرمان find نام فایلها را به شمار میآورد و آنها را با یک کاراکتر سطر جدید(قرار دادن هر نام فایل در یک سطر جداگانه) از هم جدا میکند. بنابراین برای تفکیک یک رشته بزرگ به داخل یک آرایه، لازم است به Bash بگوییم هر عضو کجا به انتها میرسد. (تذکر، این یک مثال بد است، چون نام فایل میتواند شامل یک سطر جدید باشد،بنابراین جدا کردن آنها با سطر جدید نمیتواند ایمن باشد! اما مثال زیر را نگاه کنید.)
آنچه برای شکستن یک رشته به کار میرود محتوای متغیر
$IFS = . read-a ip_elements <<< "127.0.0.1"
در اینجا از متغیر
(دستور داخلی read و عملگر
میتوانستیم همین کار را با دستور find انجام بدهیم، در صورتیکه متغیر
بنابراین، آیا روشی برای دریافت لیستی از عناصر از یک برنامه خارجی ( مانند find) در یک آرایه Bash وجود دارد؟ به طور کلی، پاسخ بلی است، به شرط آنکه راه قابل اطمینانی برای جداسازی عناصر موجود باشد.
در یک حالت خاص از نام فایلها، پاسخ این مشکل، بایتهای تهی(NUL) است. یک بایت تهی، بایتی است که همه بیتهای آن صفر است: 00000000. رشتههای Bash نمیتوانند شامل بایتهای تهی باشند، به عنوان یک محصول زبان برنامهنویسی "C" : در زبان C بایت تهی برای علامت گذاری انتهای رشته به کاررفته است. از این جهت Bash که به زبان C نوشته شده و از رشتههای بومی C استفاده میکند، این رفتار را به ارث میبرد.
یک جریان داده( مانند خروجی یک فرمان، یا یک فایل ) میتواند شامل بایت تهی باشد. جریانها مانند رشتهها هستند، با سه تفاوت عمده: آنها به صورت ترتیبی خوانده میشوند(به طور معمول نمیتوانید با پرش از روی آنها عبور کنید)، آنها یک سویه میباشند( شما میتوانید از آنها بخوانید یا در آنها بنویسید، اما به طور نوعی هر دو با هم میسر نیست )، و آنها میتوانند شامل بایتهای تهی باشند.
نه نامهای فایل میتوانند شامل بایت تهی باشند( چون آنها توسط یونیکس همانند رشتههای C تکمیل شدهاند )، و نه اکثریت وسیع اقلام قابل خواندن برای انسان که شاید ما بخواهیم در یک اسکریپت ذخیره کنیم(ازقبیل نام افراد، آدرسهای IP، و غیره ). این موضوع NUL را یک نامزد عالی برای جداسازی عناصر در یک جریان ، میسازد. به طور کلی اغلب، دستوری که میخواهید خروجی آن را بخوانید، یک گزینهای خواهد داشت، که خروجیاش را به صورت جدا شده با بایت تهی، به جای سطر جدید یا کاراکتر دیگری، ایجاد میکند.
files =() while read-r -d $'\0'; do files +=("$ REPLY ")done < <( find/ foo -print0 )
این یک روش مطمئن تفکیک خروجی یک فرمان به رشتهها میباشد. به طور قابل فهمی، ابتدا کمی به هم پیچیده و گیج کننده به نظر میرسد. لذا، بیایید کمی آن را باز کنیم:
سطر اول
ما از یک حلقه while استفاده میکنیم که هر مرتبه یک دستور read را اجرا میکند. فرمان read از گزینه
وقتی read مقداری از داده ها را میخواند و با یک بایت تهی مواجه میشود، بدنه حلقه
برای انجام این کار، ما از ترکیب
و سرانجام، ترکیب دستوری
همان طور که قبلاً بیان گردید، فرمان find خود با یک گزینه
آرایهها یک لیست مطمئن از رشتهها هستند. آنها برای نگهداری چندین نام فایل، بدون عیب میباشند.
اگر شما باید یک جریان داده را به اجزاء تشکیل دهنده عناصر تفکیک نمایید، باید طریقهای برای گفتن آنکه هر عنصر از کجا شروع و به کجا ختم میگردد، وجود داشته باشد. بسیاری اوقات، بایت تهی بهترین انتخاب برای این کار میباشد.
اگر لیستی از اقلام دارید، تا آنجا که ممکن است آن را به صورت یک لیست حفظ کنید. تا موقعی که واقعاً لازم نیست، آن را در یک رشته یا فایل تخریب نکنید. اگر باید به تفصیل آن را در یک فایل بنویسید و بعداً آنرا بخوانید، مشکل جداکننده را که در بالاتوضیح داده شد، به خاطر داشته باشید.
در مستندات گنو: Arrays
در پرسش و پاسخهای رایج:
چگونه میتوانم از متغیرهای آرایهای استفاده کنم؟
چطور میتوانم از متغیرهای متغیر( متغیرهای غیرمستقیم، اشارهگرها، مرجعها ) یا آرایههای انجمنی استفاده کنم؟
چگونه میتوانم نام فایلهای شامل کاراکتر سطرجدید، فاصله، یا هردو را پیدا کرده و با آنها کار کنم؟
من متغیرهایی را در یک حلقه مقرر میکنم. چرا آنها پس از اتمام حلقه، ناگهان ناپدید میگردند؟ یا، چرا نمیتوانم دادهها را برای خواندن لولهکشی نمایم؟
استفاده از مزیت عناصر آرایهها به راستی آسان است. به علت آنکه یک آرایه وسیله مطمئن ذخیره است، ما به سادگی میتوانیم یک حلقه for را برای تکرار روی عناصر آن، به کار ببریم:
$for file in "$> { myfiles [@]} ";do > cp"$ /backups/ >file "done
به ترکیب دستوری استفاده شده برای بسط آرایه در اینجا توجه نمایید. ما شکل نقلقولی به کار بردهایم:
دو مثال زیر نتیجه یکسان دارند:
$names =( "Bob" "Peter" "$ USER ""Big Bad John" ) $for name in "$ { names [@]} ";do echo"$ name ";done
$for name in "Bob" "Peter" "$ USER " "Big Bad John"; do echo"$ name ";done
مثال اول یک آرایه به نام
به خاطر داشته باشید، بسط
مثال فوق آرایه را در یک ساختار حلقه
myfiles =( cpdb.sql home.tbz2 etc.tbz2 )"$ /backups/{ myfiles [@]} "
این مثال، دستور cp را، با تعویض عبارت
cp"db.sql" "home.tbz2" "etc.tbz2" /backups/
فرمان cp فایلها را به دایرکتوری /backups/ شما کپی خواهد نمود.
همچنین میتوانیدعناصر منفرد آرایه را با ارجاع به شماره عضویت آنها(که index یا شاخص نام دارد)، بسط بدهید. به خاطر داشته باشید، که به طور پیشفرض،آرایهها zero-based میباشند، یعنی شماره شاخص اولین عضو آنها صفر میباشد:
$ echo "The first name is:$ { names[0] } " $ echo "The second name is:$ { names[1] } "
( میتوانید آرایهای بدون عضو شماره صفر ایجاد کنید. آنچه قبلاً در مورد آرایههای پراکنده گفتیم را به خاطر بیاورید --شما میتوانید بین شاخصها حفره داشته باشید--، و این مطلب در ابتدای آرایه نیز به همان خوبی صدق میکند. این وظیفه شما به عنوان برنامهنویس است که بدانیدکدامیک از آرایههای شما به طور بالقوه پراکنده است، و کدامیک اینطور نیست.)
روش دیگری نیز برای بسط تمام عناصر آرایه وجود دارد، که به شکل "
$names =( "Bob" "Peter" " $ USER " "Big Bad John") $ echo "Today's contestants are:$ { names [*] } "Today's contestants are: Bob Peter lhunath Big Bad John
توجه نمایید که در رشته حاصل شده، راهی برای گفتن آنکه نامها، کجا شروع و کجا ختم گردیدهاند، وجود ندارد! این است چرایی آنکه، هر چیزی را تا آنجا که ممکن است، جدا نگاه میداریم.
به خاطر داشته باشید، هنوز هم به دقت نقلقولی نمایید! اگر
میتوانید متغیر
$names =( "Bob" "Peter" " $ USER " "Big Bad John") $( echo "Today's contestants are:IFS =,;$ { names [*] } ") Today's contestants are: Bob,Peter,lhunath,Big Bad John
توجه نمایید که در این مثال، چگونه جمله IFS=,; echo ... را با قرار دادن بین
افسوس، بسط "
فرمان printf در اینجا سزاوار یک یادآوری میباشد، زیرا روش فوق العاده برازنده نسخه برداری از یک آرایه است:
$names =( "Bob" "Peter" " $ USER " "Big Bad John") $ printf"%s\n" "$ { names [@] } "Bob Peter lhunath Big Bad John
البته یک حلقه
$ printf"%s\0" "$ { myarray [@] } "> myfile
یک نکته پایانی: شما میتوانید تعداد عناصر یک آرایه را با استفاده از
$array =( a b c ) $ echo ${ # array [@]} 3
تکرار مفید:
همیشه بسط آرایهها را به طور صحیح نقلقولی کنید، درست همانطور که بسط پارامترهای معمولی را نقلقولی میکنید .
از
تا همین اواخر، BASH فقط از اعداد( به طور دقیقتر، اعداد صحیح مثبت ) میتوانست برای شاخص آرایهها استفاده کند. به این معنی که نمیتوانستید یک رشته را با دیگری ترجمه یا ترسیم کنید . این به عنوان یک کمبود احساس میشد. اشخاصی به منظور آدرسدهی به یک موضوع، سوءمصرف از متغیرهای غیرمستقیم را آغاز کردند .
پس از انتشار BASHنگارش 4، دیگر بهانهای برای استفاده از متغیر غیر مستقیم( یا بدتر از آن، eval) برای این منظور نیست. اکنون شما میتوانید آرایههای انجمنی خوشساخت را به کار ببرید.
برای ایجاد یک آرایه انجمنی، باید آرایه به صورت( declare
$ declare-A fullNames $fullNames =( ["lhunath"]="Maarten Billemont" ["greycat"]="Greg Wooledge" ) $ echo "Current user is:$ USER . Full name:$ { fullNames [$ USER ]} ."Current user is: lhunath. Full name: Maarten Billemont.
با همان دستور زبانی که برای آرایههای شاخصدار استفاده میشد، میتوانید تکرار روی کلیدهای آرایههای انجمنی را انجام دهید:
$for "user in$ { ! fullNames [@]} " >do echo "User:$ user , full name: ${ fullNames [$ user ]} ."; done User: lhunath, full name: Maarten Billemont. User: greycat, full name: Greg Wooledge.
در اینجا دو مورد یادآوری: اول، ترتیب بازیابی کلیدها از یک آرایه انجمنی، با کاربرد ترکیب دستوری
دوم، وقتی از پارامترها به عنوان کلید آرایه انجمنی استفاده میکنید، نمیتوانید از علامت
اجازه دهید با مثال تشریح کنیم:
$indexedArray =( "one" "two" ) $ declare-A associativeArray =( ["foo"]= $"bar" ["alpha"]="omega" )index = 0 key = "foo" $ echo "$ { indexedArray [$ index ]} "one $ echo "$ { indexedArray [ index ]} "one $ echo "$ { indexedArray [ index + 1]} "two $ echo "$ { associativeArray [$ key ]} "bar $ echo "$ { associativeArray [ key ]} " $ $ echo "$ { associativeArray [ key + 1]} " $
به طوری که میتوانید ملاحظه کنید، هم