globs - آموزش اسکریپت نویسی
X
تبلیغات
رایتل

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

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

#!/bin/bash

globs

glob

"glob"زیرنویس1 نام رایج برای یک مجموعه از ویژگی‌های Bash است که انواع معینی از الگوها را انطباق یا بسط می‌دهد. برخی مترادف‌ها برای globbing (نسبت به مضمونی که در آن ظاهر می‌شود) انطباق الگو، بسط الگو، بسط نام فایل، و مانند آن می‌باشند. یک glob ممکن است مانند ‎*.txt‎ به نظر آید و موقعی که برای انطباق نام فایلها به کار می‌رود، گاهی اوقات یک کاراکتر عام نامیده می‌شود.

globهای سنتی پوسته ترکیب دستوری بسیار ساده‌ای را به کار می‌برند، که نسبت به عبارت منظم کمتر گویا می‌باشند. در یک glob با اکثر کاراکترها به طور لفظی رفتار می‌شود، اما یک کاراکتر * با هیچ یا چند کاراکتر مطابقت می‌کند یک کاراکتر ‎?‎ صریحاً با یک کاراکتر مطابقت می‌کند، و ‎[...]‎ بر هر کاراکتر منفرد در یک مجموعه مشخص شده منطبق می‌گردد ( محدوده‌ها در پایین را ببینید). به طور ضمنی شروع و انتهای تمام globها مهار می‌گردد. برای مثال:

*

بر هر رشته‌ای با هر طولی منطبق می‌گردد

foo*

با هر رشته‌ای که با foo شروع می‌شود، مطابقت می‌کند

*x*

با هر رشته شامل یک x مطابقت می‌کند( ابتدا، وسط، یا انتها)

*.tar.gz

بر هر رشته‌ای که به ‎.tar.gz‎ ختم گردد، منطبق می‌شود

*.[ch]

با هر رشته‌ای که با ‎.c‎ یا ‎.h‎ خاتمه می‌یابد، تطبیق می‌کند

foo?

با foot یا ‎foo$‎ مطابقت دارد، اما با fools خیر

Bash جانشین‌هایی را که به طور غیر نقل‌قولی در فرمانها ظاهر می‌گردند، توسط تطبیق با نام فایلهای دایرکتوری جاری بسط می‌دهد. بسط glob به یک یا چند کلمه منجر می‌گردد (اگر برخی گزینه‌ها تنظیم باشند به 0 کلمه یا بیشتر)، و آن کلمات (نام فایلها) در فرمان استفاده می‌شوند. برای مثال:

tar xvf *.tar
# .بسط می‌یابد  tar xvf file1.tar file2.tar file42.tar ... به‎
# (که معمولاً چیزی نیست که کسی بخواهد)‎

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

# :این مورد حتی اگر نام فایل شامل فضای سفید باشد، مطمئن است‎
for f in *.tar; do
    tar tvf "$f"
done

# :اما این یکی  مطمئن نیست‎
for f in $(ls | grep '\.tar$'); do
    tar tvf "$f"
done

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

همچنین جانشین‌ها در Bash در چند محل برای انطباق الگو به کار می‌روند. مرسوم‌ترین آن در فرمان case می‌باشد:

case "$input" in
    [Yy]|'') confirm=1;;
    [Nn]*) confirm=0;;
    *) echo "I don't understand.  Please try again.";;
esac

الگوها (که توسط کاراکترهای | جدا می‌شوند) در برابر اولین کلمه پس از خود case مطابقت داده می‌شوند. اولین الگویی که منطبق ‌گردد، «پیروز می‌شود»، باعث اجرا شدن فرمانهای متناظر می‌گردد.

Bash همچنین اجازه می‌دهد جانشین‌ها در طرف راست یک مقایسه درون یک فرمان ‎[[‎ ظاهر بشوند:

if [[ $output = *[Ee]rror* ]]; then ...

سرانجام، globها در جریان بسط پارامتر برای نشان دادن الگوهایی که ممکن است در حین یک جایگزینی زدوده یا تعویض بشوند، به کار می‌روند. مثالهای ساده (در صفحه ارجاع شده قبلی تعداد بسیار بیشتری وجود دارد):

filename=${path##*/}    # مطابقت نماید را حذف می‌کند(حریص است)‏ ‎*/‎ الگویی که از ابتدا با‎
dirname=${path%/*}      # شود را حذف می‌کند (حریص نیست)‏ ‎/*‎ الگویی که از انتهامنطبق بر‎

printf '%s\n' "${arr[@]}"          # نسخه برداری یک آرایه، هر عضو در یک سطر‎
printf '%s\n' "${arr[@]/error*/}"  # در صورت انطباق ‎error*‎ رونوشت آرایه، حذف‎

(مرجع: آرایه‌ها، نقل‌قول‌ها، و printf.)

محدوده‌ها

جانشین‌ها با استفاده از کروشه‌ها می‌توانند یک محدوده یا class کاراکترها را تعیین کنند. این مطلب توانایی تطابق در برابر یک مجموعه از کاراکترها را به شما ارائه می‌کند. برای مثال:

[abcd]

با a یا b یا c یا dمطابقت می‌کند

[a-d]

در صورتی که منطقه شما C یا POSIX باشد، همانند مورد فوق است. در غیر اینصورت، Implementation-defined می‌باشد.

[!aeiouAEIOU]

با هر کاراکتری به غیر از a, e, i, o, u‎ و حروف بزرگ همتای آنها مطابقت دارد

[[:alnum:]]

با هر کاراکتر الفبا عددی (حرف ویا عدد) در منطقه جاری مطابقت می‌کند

[[:space:]]

بر هر کاراکتر فضای سفید منطبق می‌شود

[![:space:]]

بر هر کاراکتری که فضای سفید نیست انطباق دارد

[[:digit:]_.]

با هر رقم، یا _ یا . مطابقت می‌کند

Implementation-defined به معنای آن است که ممکن است در یک ماشین چنانکه انتظار دارید کار کند، اماروی یک ماشین دیگر نتایج کاملاً متفاوتی ارائه کند. از ترکیب ‎m-n‎ استفاده نکنید مگر آنکه اول به طور صریح منطقه خود را به C تنظیم کرده باشید، یا ممکن است نتایج غیره منتظره به دست آورید. در صورتیکه میسر باشد، عبارت‌های کلاس کاراکتری POSIX باید ترجیح داده شوند.

گزینه‌هایی که رفتار جانشینی را تغییر می‌دهند

extglob

علاوه بر globهای سنتی (پشتیبانی شده توسط تمام پوسته‌های هم خانواده Bourn) که تا اینجا دیده‌ایم، Bash(و پوسته Korn) ‏globهای توسعه یافته را ارائه می‌کند که دارای قدرت حاکی از عبارتهای منظم می‌باشد. پوسته Korn به طور پیش‌فرض اینها را فعال می‌کند، در Bash، شما باید برای فعال کردن آنها فرمان زیر را در پوسته (یا در آغاز اسکریپت) اجرا کنید:

shopt -s extglob

مرجع انطباق الگو، ترکیب دستوری ارائه شده در اینجا را تشریح می‌کند:

‎?(pattern-list)‎
با هیچ یا یک موردِ وجودِ الگوهای داده شده منطبق می‌گردد.
‎*(pattern-list)‎
با هیچ یا چند مورد از حضور الگوهای داده شده مطابقت دارد.
‎+(pattern-list)‎
با حضور یک یا چند مورد از الگوهای داده شده مطابقت می‌کند.
‎@(pattern-list)‎
با حضور تنها یکی از الگوهای ارائه شده مطابقت می‌کند.
‎!(pattern-list)‎
با هر موردی که هیچ یک از الگوها حضور نداشته باشند، منطبق می‌شود.

الگوها در یک لیست، با کاراکترهای | جدا می‌شوند.زیرنویس 2

globهای توسعه یافته به شما اجازه می‌دهند تعدادی از مشکلات را حل کنید که در غیر اینصورت نیاز به مقدار نسبتاً فوق‌العاده‌ای کد ناخوش‌آیند دارد، برای مثال:

# مطابفت می‌کنند ‎*.jpg‎ برای حذف تمام فایلها غیر از آنهایی که با‎
rm !(*.jpg)
# ‎*.jpg‎ و ‎*.gif‎ و ‎*.png‎ حذف تمام فایلها غیر از‎
rm !(*.jpg|*.gif|*.png)

#  به استثنای یکی از آنها به آدرس مورد نظر MP3 برای کپی تمام فایلهای ‎‎
cp !(04*).mp3 /mnt

برای استفاده از یک extglob در بسط پارامتر (این مورد با read می‌تواند در یک جمله BASH نیز انجام بشود):

# برای زدودن فضاهای سفید از ابتدا و انتهای یک متغیر‎
x=${x##+([[:space:]])}; x=${x%%+([[:space:]])}

الگوهای glob توسعه یافته، می‌توانند تودرتو نیز باشند.

[[ $fruit = @(ba*(na)|a+(p)le) ]] && echo 'Nice fruit'

به دلیل اینکه گزینه extglob روش تجزیه شدن برخی کاراکترها را تغییر می‌دهد، داشتن یک سطرجدید (نه فقط یک سمی کالن ) میان فرمان shopt و هر فرمانی که متعاقب globهای توسعه یافته به کار می‌رود، لازم می‌باشد. بعلاوه، نمی‌توانید ‎shopt -s extglob‎ را داخل یک بلوک دستور که globهای توسعه یافته را استفاده می‌کند، قرار بدهید، زیرا بلوک موقعی که متمایز می‌شود باید به عنوان یک کل تجزیه بشود، فرمان shopt تا وقتی بلوک ارزیابی می‌شود، یعنی جایی که دیگر خیلی دیر است، تأثیری نخواهد داشت. در حقیقت چون bash تمامیت بلوک دستور را قبل از هرگونه ارزیابی آن، تجزیه می‌کند، لازم است شما extglob را بیرون از خارجی‌ترین بلوک قرار بدهید.

بنابراین، اگر از این گزینه در اسکریپت استفاده می‌کنید، بهتر است آن را درست زیر سطر شبانگ، یا هر چه نزدیکتر به آن در حالیکه بازهم می‌توانید رسیدن به هدف را تأمین کنید، قرار بدهید.

#!/usr/bin/env bash
# Copyright (c) 2012 Foo Corporation
shopt -s extglob   #  nullglob dotglob و سایرین از قبیل‎ 

اگر کد شما یک اسکریپت نیست، اما در عوض source می‌شود، و خودش باید extglob را برقرار کند:

SourcedFile.sh
    if ! shopt extglob; then
      ClearExtGlob_SourcedFile_sh=1
      shopt -s extglob
    fi
   # تا extglob مفهوم اصلی نهفته در پسِ گزینه‌های پایین آن است که تجزیه ‎
   #  زمان ارزیابی به تعویق افتد‎
   declare -a s='( !(x) )'
   echo "${a[@]}"

   echo "${InvalidVar:-!(x)}"

   eval 'echo !(x)'  # using eval if no other option.

   if [ "${ClearExtGlob_SourcedFile_sh}" == "1" ]; then
     unset ClearExtGlob_SourcedFile_sh
     shopt -u extglob
   fi

test.sh
   shopt -u extglob
   if true; then
     source ./SourcedFile.sh
   fi

nullglob

اگر یک glob در تطابق با هر نام فایلی ناموفق باشد، به طور معمول پوسته آن را به تنهایی باقی می‌گذارد. این مطلب به معنای آن است که glob خام به فرمان عبور داده می‌شود، به این صورت:

$ ls *.ttx
ls: cannot access *.ttx: No such file or directory

این مورد به فرمان اجازه می‌دهد که جانشینی که شما استفاده کرده‌اید را ببیند، و در یک پیغام خطا آن را به کار ببرد. اگر در Bash گزینه nullglob تنظیم شود، در هر حال، یک glob که با هیچ فایلی منطبق نمی‌گردد کاملاً حذف می‌شود.این مطلب در اسکریپت‌ها مفید است، اما در خط فرمان تا اندازه‌ای گیج‌کننده می‌باشد، چون انتظارات بسیاری از ابزارهای استاندارد را نقض می‌کند (برای یک جایگزین بهتر failglob را در ادامه ببینید):

# در اسکریپت‌ها مناسب است‎
shopt -s nullglob
oggs=(*.ogg)
for ogg in "${oggs[@]}"; do ...

# !در خط فرمان نامناسب‎
shopt -s nullglob
ls *.ttx
# را بدون شناسه اجرا می‌کند و تمام موارد را لیست می‌کند "ls" فرمان‎

failglob

اگر یک الگو در انطباق ناموفق شود، bash یک خطای بسط گزارش می‌کند. این مورد در خط فرمان می‌تواند مفید باشد:

# !مناسب در خط فرمان‎
$ touch *.foo   # را ایجاد می‌کند '*.foo' اگر در انطباق جانشین ناموفق شود فایل‎
$ shopt -s failglob
$ touch *.foo   # اجرا نمی‌شود touch با تنظیم گزینه فوق فرمان‎
-bash: no match: *.foo

dotglob

طبق قرارداد، فایلی که نام آن با یک نقطه (dot) شروع می‌شود فایل پنهان است و توسط ls نشان داده نمی‌شود. globbing همان قرارداد را به کار می‌برد -- نام فایلهای شروع شده با نقطه به وسیله یک glob منطبق نمی‌شوند، مگر اینکه glob نیز با یک نقطه شروع بشود. Bash دارای یک گزینه dotglob می‌باشد که انطباق «فایلهای نقطه‌ای» را اجازه می‌دهد:

shopt -s dotglob nullglob
files=(*)
echo "There are ${#files[@]} files here, including dot files and subdirs"

لازم است توجه شود که وقتی dotglob فعال می‌شود، * با فایلهایی مانند ‎.bashrc‎ منطبق می‌گردد، اما با دایرکتوری‌های . یا .. خیر. این کاملاً مستقل از مشکل انطباق «فقط فایلهای نقطه‌ای» است -- یک glob به صورت ‎.*‎ با موارد . و .. نیز منطبق خواهد شد، معمولاً باعث مشکلاتی می‌گردد. بخش بعد را ببینید.

GLOBIGNORE

متغیر GLOBIGNORE متعلق به Bash (نه shopt) به شما اجازه می‌دهد الگوهایی را که یک glob نباید تطبیق ب دهد، تعیین نمایید. این گزینه به شما اجازه می‌دهد مشکل مفتضح «من می‌خواهم تمام فایلهای نقطه‌ای منطبق شوند اما . یا .. خیر» را رفع و رجوع کنید:

$ echo .*
. .. .bash_history .bash_logout .bashrc .inputrc .vimrc
$ GLOBIGNORE=.:..
$ echo .*
.bash_history .bash_logout .bashrc .inputrc .vimrc


CategoryShell

glob (آخرین ویرایش ‎2012-07-21 11:21:48‎ توسط 77)


  1. مترجم: من در مواردی صرفاً کلمه «جانشین» را به جای نام glob و «جانشینی» را برای کلمه globbing به کار می‌برم و اکیداً از کلمه دیگری به این منظور استفاده نمی‌کنم. (بازگشت)

  2. مترجم: اگر این شرح مختصر یک سطری کمی نا رسا می‌باشد و منظور را به خوبی نمی‌رساند من در اینجا با ذکر مثال سعی در گویاتر نمودن آن کرده‌ام.

     $ mkdir mytest
     $ cd mytest
     $ touch {,a,b}{2,a2,12,c,aaa,bbbb}
     $ ls
     12  2  a12  a2  aa2  aaa  aaaa  abbbb  ac  b12  b2  ba2  baaa  bbbb  bbbbb  bc  c   # ایجاد گردیده‌اند touch فایلهایی که با فرمان‎
     $ echo ?(a|b)2
     2 a2 b2 ‎
     $ echo *(a|b)2
     2 a2 aa2 b2 ba2
     $ echo +(a|b)2
     a2 aa2 b2 ba2
     $ echo @(a|b)2
     a2 b2
     $ echo !(a|b)2
     12 2 a12 aa2 b12 ba2
    

    در اینجا لیست الگوها ‎(a | b)‎ می‌باشد. چنانکه در متن اشاره شد ‎?(a | b)2‎ یعنی یک یا هیچ مورد انطباق و همانطور که در خروجی ملاحظه می‌کنید 2 مصداق هیچ مورد انطباق(هیچ یک از a یا b حضور ندارند) و a2 و b2 مصداق یک مورد انطباق می‌باشند که توسط فرمان نمایش داده شده‌اند. اما به عنوان مثال aa2 نمایش داده نشده چون مصداق این glob نیست (چون a دو بار حضور دارد)، برای اینکه بیش از یک مورد تطابق الگوی a در آن وجود دارد.
    در مورد بعدی یعنی ‎*(a | b)2‎ هیچ یا چند مورد انطباق قابل قبول است بنابراین موارد aa2 و ba2 نیز نمایش داده می‌شوند. چهار مورد دیگر نیز به همین ترتیب نتایجی در خروجی نمایش می‌دهند که بر اساس تعریف آنها در متن می‌باشد. اکنون با بررسی دقیق خروجی‌ها در مقایسه با فایلهای موجود در پوشه جاری، بهتر متوجه عملکرد globهای توسعه یافته، می‌شوید

    و یک مثال دیگر با تعداد فایلهای بیشتر:

     $ pwd     # دقت کنید که در همان دایرکتوری باشید‎
     ~/mytest
     $ rm ./*
     $ touch {,a,b}{2,a2,12,c,aaa,bbbb,222,ab23333}
     $ ls      # لیست فایلهای ایجاد شده با دستور فوق را مشاهده می‌کنید‎
     12  222  a2    aa2  aaaa      ab23333  ac   b2    ba2   bab23333  bbbbb  c  2   a12  a222  aaa  aab23333  abbbb    b12  b222  baaa  bbbb      bc
     $ echo ?(a|b)2*    # در این مثال کاراکتر عام * را نیز به کار برده‌ایم‎
     2 222 a2 a222 b2 b222
     $ echo *(a|b)2*
     2 222 a2 a222 aa2 aab23333 ab23333 b2 b222 ba2 bab23333
     $ echo +(a|b)2*
     a2 a222 aa2 aab23333 ab23333 b2 b222 ba2 bab23333
     $ echo @(a|b)2*
     a2 a222 b2 b222
     $ echo !(a|b)2*
     12 2 222 a12 a222 aa2 aab23333 ab23333 b12 b222 ba2 bab23333
    

    البته توجه دارید که بخش بعد از پرانتز بسته بخشی از glob توسعه یافته نیست و برای هر پنج مورد ثابت است. (بازگشت)