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

پرسش و پاسخ شماره ۲۰


پرسش و پاسخ‌های رایج Bash  در Greg\'s Wiki

پرسش و پاسخ شماره ۲۰

چگونه می‌توانم نام فایلهای شامل کاراکتر سطرجدید، فاصله، یا هردو را پیدا کرده و با آنها کار کنم؟

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

شیوه برگزیده کار با نامهای انتخابی فایل، بازهم استفاده از ‎find(1)‎ می‌باشد:

find ... -exec command {} \\;

یا، اگر به مدیریت نام‌فایلها به طور دسته جمعی نیاز دارید:

find ... -exec command {} +

xargs به ندرت بتواند سودمندتر از مورد فوق باشد، اما اگر شما حقیقتاً اصرار دارید، استفاده از ‎ -0‎ را به خاطر داشته باشید:

# نیاز دارد GNU/BSD درfind و xargs به فرمان 
find ... -print0 | xargs -0 command

# استفاده نکنید xargs هرگز بدون گزینه 0- یا ضمائم مشابه از 

یکی از اینها را به کار ببرید مگر اینکه واقعاً نمی‌توانید.

روش دیگر برای کار با فایلهایی که در نامشان دارای فاصله می‌باشند استفاده از بسط نام فایل پوسته(globbing) است. اشکال آن، کار نکردن به طور بازگشتی است(به استثنای بسط‌های zsh یا globstar نگارش 4 از bash)، اما اگر شما فقط نیاز به پردازش تمام فایلهای یک دایرکتوری منفرد دارید، به طور خارق‌العاده‌ای خوب کار می‌کند.

برای مثال، این کُد تمام فایلهای ‎ *.mp3‎ در دایرکتوری فعلی را با جایگزینی خط‌زیر(underscore) به جای فاصله در نامشان تغییر نام می‌دهد:

# Bash/ksh
for file in ./*\\ *.mp3; do
  mv \"$file\" \"${file// /_}\"
done

برای مثالهای بیشتر تغییر نام فایلها، پرسش و پاسخ شماره 30 را ملاحظه نمایید.

به خاطر داشته باشید، لازم است شماتمام بسط‌های پارامتر را با استفاده از نقل‌قول دوگانه نقل‌قولی کنید. اگر چنین نکنید، بسط تحت تأثیر تفکیک کلمه قرار می‌گیرد(همچنین بخش جداسازی شناسه و تله‌هایBash را ببینید). همچنین، همیشه جانشین‌ها را با ‎ \"./\"‎ پیشوند کنید، در غیر این صورت، اگر فایلی با نام شامل \"-\" به عنوان اولین کاراکتر نام وجود داشته باشد بسط‌ها ممکن است به طور غلط، به عنوان گزینه تفسیر گردند.

یک روش دیگر کار با نام فایلها به طور بازگشتی، شامل استفاده از گزینه ‎ -print0‎ فرمان find (الحاقیه GNU/BSD )، همراه با گزینه ‎ -d‎ پوسته bash برای read می‌باشد:

# Bash
unset a i
while IFS= read -r -d $\'\\0\' file; do
  a[i++]=\"$file\"        # or however you want to process each file
done < <(find /tmp -type f -print0)

مثال اسبق تمام فایلهای دایرکتوری ‎/tmp‎ را (به طور بازگشتی) در یک آرایه حتی اگر در نام آنها سطرجدید یا فضای سفید موجود باشد، می‌خواند، از طریقِ اجبار read به استفاده از بایت تهی‎(\\0)‎ به عنوان جداکننده سطر. چون NUL بایت معتبر در نام فایل یونیکس نمی‌باشد، این رویکرد در کنار استفاده از find -exec مطمئن‌ترین روش است. ‎IFS=‎ برای اجتناب از بریدن فضای سفید ابتدا و انتها لازم است، و ‎ -r‎ برای اجتناب از پردازش کاراکتر \\ نیاز می‌باشد. در حقیقت،‎ $\'\\0\'‎ معادل \'\' است، بنابراین می‌توانستیم آن را به این شکل نیز بنویسیم:

# Bash
unset a i
while IFS= read -r -d \'\' file; do
  a[i++]=\"$file\"
done < <(find /tmp -type f -print0)

پس چرا این کار نمی‌کند؟

# کار نمی‌کند
unset a i
find /tmp -type f -print0 | while IFS= read -r -d \'\' file; do
  a[i++]=\"$file\"
done

به علت وجود لوله، تمامیت حلقه while در یک پوسته فرعی‎ انجام می‌شود و بنابراین تخصیص‌های آرایه پس از خاتمه یافتن حلقه از دست خواهد رفت. (برای جزئیات بیشتر در این باره، پرسش و پاسخ شماره 24 را ملاحظه کنید.)


CategoryShell

پرسش و پاسخ 20 (آخرین ویرایش ‎ 2012-06-29 11:53:50 ‎ توسط GreyCat)