یک توصیه که برنامهنویسان تازهکار bash بارها و بارها میشنوند:
متغیرهای خود را نقلقول کنید!
چرا؟
وقتی متغیری (مثل $name) را بسط میدهید، تقریباً همیشه میخواهید از مقدار متغیر بهصورت یک رشتهی واحد (یا یک عدد) استفاده کنید.
نمیخواهید مقدار متغیر بهطور غیرمنتظره بهصورت دو رشتهی جداگانه یا بیشتر در نظر گرفته شود.
نقلقول سازوکار اصلی است برای اطمینان از اینکه یک تکه متن بهصورت یک واژهی واحد در نظر گرفته میشود. دو نوع اصلی نقلقول وجود دارد.
نقلقول دوگانه امکان بسط پارامتر، بسط حسابی و جایگذاری دستور را در درون خود فراهم میکند.
echo "This is bash version $BASH_VERSION"
میتوانید با قرار دادن یک بکاسلش پیش از یک نقلقول دوگانه، آن را درون یک رشتهی دوگانهنقلقولشده بگنجانید.
echo "She said \"Let me teach you about bash.\""
نقلقول تکگانه هیچ بسطی را در درون خود اجازه نمیدهد.
echo 'This is bash version $BASH_VERSION'
# ...^..................................^
# prints exactly: This is bash version $BASH_VERSION
گنجاندن یک نقلقول تکگانه درون یک رشتهی تکنقلقول ممکن نیست.
دقیقاً چه اشکالی در echo $name وجود دارد؟
وقتی شل یک خط کد را تجزیه میکند، چند نوع بسط را به ترتیب انجام میدهد. دو مورد از آخرین بسطها تقسیم واژه و بسط نام فایل هستند.
هر نتیجهای از بسط پارامتر، جایگذاری دستور یا بسط حسابی که درون نقلقول دوگانه رخ نداده باشد مشمول «تقسیم واژه» میشود.
شل نتیجهی بسطدادهشده را برمیدارد و آن را بر اساس هر دنبالهای از نویسهها که در مقدار $IFS وجود دارند تقسیم میکند.
بهطور پیشفرض، اینها فاصله، تب و نویسهی خط جدید هستند.
میتوانیم این را با یک حلقهی شل در عمل ببینیم:
sentence="The quick brown fox jumps."
i=0
for word in "$sentence"; do echo "$((++i)) $word"; done
# ..........^.........^
خروجی:
1 The quick brown fox jumps.
اما بدون نقلقول:
i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^
خروجی:
1 The
2 quick
3 brown
4 fox
5 jumps.
بیایید آن را با مقدار متفاوتی از $IFS ببینیم:
csv="first,second,third"
IFS=","
for word in $csv; do echo "$word"; done
خروجی:
first
second
third
یک تکنیک برای غیرفعال کردن تقسیم واژه این است که $IFS را روی یک رشتهی خالی تنظیم کنید
sentence="The quick brown fox jumps."
IFS= # or explicitly, IFS=""
i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^
خروجی:
1 The quick brown fox jumps.
با این حال، نقلقول نکردن متغیرها توصیه نمیشود، مگر اینکه بسط نام فایل را هم غیرفعال کنید. خواهیم دید که چطور این کار را در ادامه انجام دهیم.
الگوهای «Glob» برای بیان یک الگوی مختصر جهت تطبیق با مجموعهای از فایلها به کار میروند.
برای مثال، میتوانید همهی فایلهای Markdown یک پوشه را با *.md تطبیق دهید.
الگوهای Glob از زمان اختراع Unix وجود داشتهاند.
نویسههای عام Glob عبارتاند از:
* با صفر یا چند نویسه از هر نوع تطبیق مییابد،
? دقیقاً با یک نویسه از هر نوع تطبیق مییابد،
[...] دقیقاً با یک نویسه از مجموعهی نویسههای فهرستشده درون کروشه تطبیق مییابد.
[abc] دقیقاً با یک نویسه تطبیق مییابد که یا a باشد یا b یا c.
^ اختیاری)
[][] یک مجموعهی نویسه است که با کروشهی بسته یا باز تطبیق مییابد.[[\]]
[x-y] دقیقاً با یک نویسه تطبیق مییابد که در بازهی x تا y باشد
^ اختیاری) یا آخرین نویسه پیش از کروشهی بسته.
[-xy] یا [xy-] هرکدام دقیقاً با یک نویسه تطبیق مییابند که - یا x یا y باشد[x\-y]
[0-9A-Fa-f] با یک رقم هگزادسیمال تطبیق مییابد[^abc] دقیقاً با یک نویسه تطبیق مییابد که a یا b یا c نیست،[^x-y] دقیقاً با یک نویسه تطبیق مییابد که در بازهی x تا y نیست
[[:character_class:]] با یک نویسه تطبیق مییابد که در «character_class» نامگرفته باشد
lower -- حروف کوچکupper -- حروف بزرگalpha -- حروفdigit -- ارقامalnum -- حروف و ارقامspace -- فاصلهی سفیدblank -- فاصلهی سفید افقی (فاصله و تب)xdigit -- ارقام هگزادسیمالword -- نویسههای مجاز در یک شناسه (حروف، اعداد، زیرخط)punct -- نقطهگذاریcntrl -- نویسههای کنترلیgraph -- نویسههای «قابلمشاهده»print -- نویسههای قابلمشاهده بهعلاوهی فاصله[[:alpha:]] از [[:lower:][:upper:]] تشکیل شده است[[:alnum:]] از [[:alpha:][:digit:]] تشکیل شده است[[:word:]] از [[:alnum:]_] تشکیل شده است[[:word:]] است و هم [[:punct:]]
[[:graph:]] از [[:alnum:][:punct:]] تشکیل شده است[[:print:]] از [[:graph:] ] تشکیل شده است -- فقط فاصله (اکتال ۰۴۰) و نه هیچ فاصلهی سفید دیگری.میتوانید با یک حلقهی for روی فهرستی از فایلها پیمایش کنید
for file in *.csv; do
do_something_with "$file"
done
دقت کنید که متغیر "$file" نقلقول شده است.
الگوی Glob ممکن است فایلهایی را بازگرداند که در اسمشان فاصله دارند.
باید متغیر را نقلقول کنیم تا دستور do_something_with دقیقاً یک آرگومان دریافت کند: اسم فایل.
الگوهای Glob عبارت باقاعده نیستند.
.
?
.*
*
.* یعنی «با اسم فایلی تطبیق بیاب که اولین نویسهاش یک نقطهی تحتاللفظی باشد و پس از آن صفر یا چند نویسه از هر نوع بیاید»..+
?*
Bash الگوهای توسعهیافته را فراهم میکند که به عبارتهای باقاعده نزدیکترند.
زمانهایی هست که میخواهید بسط نام فایل را سرکوب کنید. پردازش ورودی کاربر یکی از همان زمانها است. این تکهکد را در نظر بگیرید
read -p "Enter something: " user_input
echo $user_input
اگر کاربر یک ستاره (*) وارد کند، خروجی چه خواهد بود؟
فهرست فایلهای پوشهی جاری.
احتمالاً این کاری نیست که میخواهید انجام دهید.
مثال دیگر، فرستادن الگوها بهعنوان آرگومان به برنامههایی است که انتظار الگو دارند.
برنامهی tr اغلب برای دستکاری متن به کار میرود. برای کوچک کردن یک مقدار متغیر میتوانید این کار را بکنید
var="My Puppet"
echo "$var" | tr '[:upper:]' '[:lower:]'
خروجی:
my puppet
یک اشتباه رایج این است که نقلقول کردن آرگومانهای tr را فراموش کنید: tr [:upper:] [:lower:]
چرا این مسئلهساز است؟
الگوهای Glob بدون نقلقول به فهرست فایلهای منطبق بسط داده میشوند.
فرض کنید شخصی که از اسکریپت شما استفاده میکند، فایلهایی با اسمهای p و u و w در پوشهی جاری داشته باشد:
touch p u w
var="My Puppet"
echo "$var" | tr [:upper:] [:lower:]
این کار به خطا میانجامد!
الگوی [:upper:] بدون نقلقول با هرکدام از :, u, p, e یا r تطبیق مییابد.
الگوی [:lower:] بدون نقلقول با هرکدام از :, l, o, w, e یا r تطبیق مییابد.
Bash برای الگوی اول با فایلهای p و u و برای الگوی دوم با فایل w تطبیق میدهد، و tr به این شکل فراخوانی میشود
echo "$var" | tr p u w
و این تعداد آرگومان اشتباهی برای tr است.
میتوانید از دستور set برای غیرفعال کردن بسط نام فایل استفاده کنید.
echo * # prints a list of files
set -f # disable filename expansion
echo * # prints a literal asterisk
set +f # enable filename expansion
چندین تنظیم برای دستور داخلی shopt وجود دارد که نحوهی کار بسط نام فایل را کنترل میکنند.
چند مورد جالبشان عبارتاند از:
shopt -s nocaseglob -- تطبیق بدون حساسیت به بزرگی و کوچکی حروف را انجام میدهدshopt -s extglob -- الگوهای توسعهیافته را فعال میکندshopt -s nullglob -- اگر هیچ فایلی منطبق نشود، الگو را پاک میکند.
رفتار پیشفرض این است که الگو را بهصورت یک رشتهی تحتاللفظی سر جایش بگذارد.