در Bash، میتوانید این کار را به طور مطمئن و به آسانی با گزینههای nullglob و dotglob (که رفتار globbing را تغییر میدهند) و یک آرایه انجام دهید:
# Bash shopt -s nullglob dotglob files=(*) (( ${#files[*]} )) || echo directory is empty shopt -u nullglob dotglob
البته شما میتوانید از هر جانشینی به جای * استفاده کنید. برای مثال *.mpg یا /my/music/*.mpg هم به خوبی کار میکنند.
به خاطر داشته باشید که شما نیازمند مجوز خواندن آن دایرکتوری هستید، در غیر آنصورت همواره خالی به نظر میآید.
بعضی ها گزینه nullglob را دوست ندارند، چون ناپدید شدن جانشینهایی که انطباق نمییابند، نتایج برنامههایی همچون ls را کاملاً مخدوش میسازد. تایپ اشتباه ls *.zip به صورت ls *.zpi میتواند باعث شود تمام فایلها نمایش داده شوند(برای چنین موقعیتهایی تنظیم گزینه failglob را ملاحظه کنید). تنظیم nullglob در پوسته فرعی از تغییر ناخواسته آن در تمام پوسته، به قیمت یک فرآیند اضافی fork() اجتناب میکند. اگر شما تمایل به اجتناب از فعال و غیر فعال کردن گزینهها در پوسته دارید، میتوانید تمام آن را در یک پوسته فرعی قرار بدهید:
# Bash if (shopt -s nullglob dotglob; f=(*); ((! ${#f[@]}))); then echo "The current directory is empty." fi
اشکال دیگر این راهکار(گذشته از فرآیند اضافی fork()) آنست که وقتی پوسته فرعی خارج میشود، آرایه از دست رفته است. اگر شما برای استفاده بعدی از آن نام فایلها طرحی داشته باشید، دوباره باید تمام آنها بازیابی بشوند.
این مثالها هر دو یک جانشین(glob) را بسط میدهند ونامفایلهای حاصل را در یک آرایه ذخیره میکنند، و سپس بررسی میکنند که آیا تعداد عناصر آرایه 0 است. اگر واقعاً میخواهید ببینید چه تعداد فایل در آنجا هست، به جای بررسی اینکه آیا تعداد عناصر صفر است، فقط اندازه آرایه را چاپ کنید:
# Bash در پوسته shopt -s nullglob dotglob files=(*) echo "The current directory contains ${#files[@]} things."
همچنین اگر شما با قرار دادن آن جانشین منطبق نشده با نام هیچ یک از فایلها، در آرایه(به جای یک آرایه خالی) موافق هستید، میتوانید از nullglob اجتناب نمایید:
# Bash در پوسته shopt -s dotglob files=(*) if [[ -e ${files[0]} || -L ${files[0]} ]]; then echo "The current directory is not empty. It contains:" printf '%s\n' "${files[@]}" fi
بدون گزینه nullglob، اگر هیچ فایلی در دایرکتوری نباشد، جانشین به عنوان تنها عنصر به آرایه اضافه میشود. چون * یک نامفایل معتبر است، نمیتوانیم بررسی کنیم که آیا آرایه محتوی یک کاراکتر * است. بنابراین به جای آن، ما بررسی میکنیم آیا چیزی به عنوان فایل در آرایه وجود دارد. گزینه -L برای test لازم است، چون اگر اولین فایل پیوند نمادین بدون مقصد معتبر باشد -e ناموفق میشود.
اگر لازم است که اسکریپت شما با پیادهسازیهای متنوع پوسته غیرBash اجرا بشود، میتوانید استفاده از یک برنامه خارجی مانند python، perl، یا find، را امتحان کنید، یا میتوانید یکی از اینها را آزمایش نمایید:
# POSIX در شل # Clobbers the positional parameters, so make sure you don't need them. set -- * if test -e "$1" || test -L "$1"; then echo "directory is non-empty" fi
در این مرحله، پارامترهای مکانی با محتویات دایرکتوری بار گذاری شدهاند، و میتوانند برای پردازش به کار بروند.
در پوسته Bourne، حتی وخیمتر است، زیرا test -e یا test -L وجود ندارد:
# Bourne در شل # (Of course, the system must have printf(1).) if test "`printf '%s %s %s' .* *`" = '. .. *' && test ! -f '*' then echo "directory is empty" fi
البته، اگر * در نتیجه چیز دیگری غیر از فایل ساده موجود باشد(از قبیل یک دایرکتوری یا FIFO) بررسی ناموفق میشود. فقدان گزینه -e دستور test به راستی آزار دهنده است.
هرگز تجزیه خروجی ls را امتحان نکنید. حتی راه حل ls -A میتواند نقض شود(به عنوان مثال در HP-UX، اگر شما کاربر ارشد باشید،ls -A دقیقاً نتیجه متضاد با آنچه وقتی root نباشید، دارد-- خیر، من نمیتوانم چیزی را که به شدت نابخردانه است، آرایش کنم).
در حقیقت، رویهم رفته شاید شخص تمایل به اجتناب از پرسش مستقیم داشته باشد. به طور معمول اشخاص، با توسل به پرسش بلندتر، میخواهند بدانند آیا دایرکتوری خالی است، به علت آنکه میخواهند کاری انجام دهند که با فایلها در آنجا درگیر میشود، و غیره. برای مثال، یکی از اینها مثالهای مبتنی برfind شاید راه حل مناسبی باشند:
# Bourne در پوسته find "$somedir" -type f -exec echo Found unexpected file {} \; find "$somedir" -maxdepth 0 -empty -exec echo {} is empty. \; # GNU/BSD find "$somedir" -type d -empty -exec cp /my/configfile {} \; # GNU/BSD
تقریباً به طور همیشگی، تمام آنچه واقعاً لازم میباشد چیزی مانند این است:
# Bourne در پوسته for f in ./*.mpg; do test -f "$f" || continue mympgviewer "$f" done
به بیان دیگر، شاید وقتی که در حقیقت نیازی به این قبیل بررسی نیست، به گمان شخص پرسش کننده، یک بررسی صریح شاخه خالی، برای اجتناب از پیغام خطایی مانند mympgviewer: ./*.mpg: No such file or directory لازم است.
پشتیبانی از ویژگیهای nullglob شکل، ناسازگار است. در ksh93 این مورد میتواند بر مبنای پیشالگو، با پیشوند نمودن ~(N) انجام شود1:
# ksh93 for f in ~(N)*; do .... done
از: http://permalink.gmane.org/gmane.comp.standards.posix.austin.general/2058، که شامل برخی گفتگوهای مفید است.
پرسش و پاسخ 4 (آخرین ویرایش 2012-01-13 09:37:49 توسط ght)