این صفحه اشتباهات رایجی را که برنامه نویسان Bash مرتکب میگردند نشان میدهد. مثالهای ذیل هر یک به نوعی معیوب هستند:
یکی از رایجترین اشتباهاتی که برنامهنویسان BASH مرتکب میشوند، نوشتن حلقهای مانند این است:
for i in $(ls *.mp3); do # Wrong! some command $i # Wrong! done for i in $(ls) # Wrong! for i in `ls` # Wrong! for i in $(find . -type f) # Wrong! for i in `find . -type f` # Wrong! files=($(find . -type f)) # Wrong! for i in ${files[@]} # Wrong!
هرگز بدون نقلقولها از یک جایگزینی فرمان -- از هر نوع! -- استفاده نکنید. در اینجا دو موضوع اصلی وجود دارد: استفاده از یک بسط غیر نقلقولی برای تفکیک خروجی به شناسهها، و تجزیه خروجی ls -- برنامهای که در هر صورت هرگز خروجیاش نباید تجزیه شود.
چرا؟ چون وقتی یک فایل در نام خود شامل فاصله باشد، این مورد ناموفق میشود. چرا؟ به علت آن که خروجی جایگزینی فرمانِ $(ls *.mp3) دستخوش تفکیک کلمه میگردد. فرض کنیم ما در دایرکتوری جاری دارای فایلی به نام 01 - Don't Eat the Yellow Snow.mp3 باشیم، حلقه for روی هر کلمه حاصل شده از نام فایل تکرار میشود: 01, -, Don't, Eat... وغیره.
شاید وخیمتر، رشتههایی که از مرحله تفکیک کلمه قبلی حاصل شدهاند سپس تحت تأثیر بسط نام مسیر واقع خواهند شد. به عنوان یک مثال، اگر ls هر خروجی شامل یک کاراکتر * تولید کند، کلمه حاوی آن به عنوان یک الگو شناسایی خواهد گردید و با لیستی از تمام نام فایلهایی که با آن مطابقت میکنند، جایگزین میشود.
همچنین شما نمیتوانید جایگزینی را با نقلقول دوگانه بپوشانید:
for i in "$(ls *.mp3)"; do # Wrong!
این باعث میگردد با کل خروجی فرمان ls به عنوان یک کلمه منفرد رفتار شود. به جای هر نوبت تکرار با هر نام فایل، حلقه فقط یک مرتبه با تمام نام فایلهای منگنه شده با یکدیگر اجرا خواهد شد.
علاوه بر این، استفاده از ls به طور قابل فهمی غیر ضروری است. این یک فرمان خارجی است که واقعاً برای انجام این کار لازم نیست. بنابراین، روش صحیح برای انجام آن، کدام است؟
اجازه بدهید Bash لیست نام فایلها را برای شما بسط بدهد. بسط، در معرض تفکیک کلمه قرار نخواهد گرفت. با هر نام فایل که با یک glob به شکل *.mp3 منطبق میشود، به عنوان یک کلمه جداگانه رفتار میگردد، و حلقه برای هر نام فایل یکبار تکرار خواهد شد. (اگرنیاز دارید فایلها را به طور بازگشتی پردازش کنید، UsingFind را ببینید.)
سؤال: اگر هیچ نام فایل مطابق جانشین *.mp3 در دایرکتوری جاری نباشد چه میشود؟ آنوقت حلقه for یکبار با i="*.mp3" اجرا میشود، که رفتار مورد انتظار نمیباشد! چاره کار، بررسی آن است که آیا یک فایل منطبق وجود دارد:
# POSIX for i in *.mp3; do [ -e "$i" ] || continue some command "$i" done
اگر شما به سادگی همیشه نقلقولها را به کار ببرید و هرگز به هر دلیل از تفکیک کلمه استفاده نکنید، خود را از بسیاری از این دامها حفظ خواهید نمود! تفکیک کلمه یک سوءویژگی ورشکسته به ارث رسیده از پوسته Bourne است که به طور پیشفرض، اگر بسطها را نقلقولی نکنید ، پیش میرود. اکثریت وسیع تلهها به طریقی به بسطهای غیر نقلقولی و تفکیک کلمه و جانشینی( globbing ) بعدی آن نتیجه، مربوط میشوند. یک گونه دیگر در این زمینه سوءمصرف تفکیک کلمه و یک حلقه for برای خواندن سطرهای یک فایل است. این اشتباه است! چنانکه آن سطرها نامهای فایل باشند، این اشتباهی مضاعف( یا شاید سهبرابر) است.
به نقلقولهای اطراف $i در بدنه حلقه توجه نمایید. این مورد به دومین pitfall ما منجر میشود:
ادامه مطلبترجمه کامل این صفحه در قالب pdf یا به صورت یک فایل html نیز قابل دریافت است.