آموزش اسکریپت نویسی

آموزش اسکریپت نویسی پوسته گنو-لینوکس

آموزش اسکریپت نویسی

آموزش اسکریپت نویسی پوسته گنو-لینوکس

پرسش و پاسخ شماره ۴



پرسش و پاسخ شماره ۴

چگونه بررسی نمایم که آیا یک شاخه، خالی است یا خیر؟ چطور هر فایل ‎*.mpg‎ را بررسی کنم، یا تعداد آنها را شمارش کنم؟

در 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


CategoryShell

  1. از: http://permalink.gmane.org/gmane.comp.standards.posix.austin.general/2058، که شامل برخی گفتگوهای مفید است.

پرسش و پاسخ 4 (آخرین ویرایش ‎ 2012-01-13 09:37:49 ‎ توسط ght)

نظرات 0 + ارسال نظر
ایمیل شما بعد از ثبت نمایش داده نخواهد شد