آموزش‌های خط فرمانی

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

آموزش‌های خط فرمانی

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

ادامه پرسش و پاسخ شماره ۵

ادامه پرسش و پاسخ شماره ۵

2.1.2. سایر روشها

گاهی اوقات زدودن سطرهای خالی واقعاً مطلوب است، یا شاید شما بدانید که همواره ورودی با سطرجدید جدا شده است، از قبیل ورودی تولید شده به طور درونی توسط اسکریپت شما. در برخی پوسته‌ها، امکان استفاده از ‎ -d‎ برای تنظیم جداکننده سطر read به null، و بعد سوء استفاده از ‎ -a‎ یا ‎-A‎(نسبت به پوسته) -- که به طور معمول برای خواندن فیلدهای یک سطر به داخل آرایه به کار می‌رود -- برای خواندن سطرها، وجود دارد. به طورقابل اجرا، با تمام ورودی به عنوان یک سطر رفتار می‌شود، و فیلدهای آن با سطرجدید جدا می‌شوند.

# Bash 4  در شل
    IFS=$'\n' read -rd '' -a lines <file

# mksh و zsh  در شل 
    IFS=$'\n' read -rd '' -A lines <file

متأسفانه، مورد فوق در ksh93 کار نمی‌کند، ولواینکه فرمان read در این پوسته نشانه جداکننده ‎ -d‎ را دارد. البته، مثالهای فوق سطرهای خالی را حفظ نمی‌کنند، اما آنها جایگزین سریع و آسان mapfile هستند که در چند پوسته غیر-bash نیز کار می‌کنند. از نگارش ‎ alpha 2012-10-12 ‎  شل ksh93 برطرف گردیده است

12-10-09 +read -d '' now reads up to a NUL byte.

2.1.3. سطرها را با for نخوانید!

هرگز سطرها را با استفاده از for در حلقه‌ها نخوانید! متکی بودن به تفکیک کلمه متغیر IFS، اگر فضای سفید جداکننده تکراری داشته باشید، باعث مسائلی می‌گردد، به دلیل آنکه آنها یکپارچه می‌شوند. به این طریق امکان حفظ نمودن سطرهای سفید به عنوان عناصر خالی آرایه وجود ندارد. حتی بدتر از این، کاراکترهای ویژه جانشینی، بدون غیرفعال کردن و فعال نمودن مجدد آن، بسط داده می‌شوند. هرگز از این راهکار استفاده نکنید، مسئله‌ساز است، روشهای عبور موقت همگی ناخوشایند هستند، و تمام مشکلات حل نمی‌شوند.

چون که چنین موردی به طور غیر قابل باوری، یک اشتباه رایج می‌باشد، مورد ذیل تقریباً بهترین بیان از این ترفند و اینکه چقدر سخت‌تر از انجام آن به طور صحیح است، را تشریح می‌کند-- و باز هم نمی‌تواند سطرهای جدید متوالی را حفظ نماید! ناسالمی‌اش از اینجا ناشی می‌شود. برای توضیحات تفصیلی بخش سطرهای جدید را با for نخوانید را ملاحظه کنید.

# Bash
# WARNING: Don't do this!

evilReadLines() {
    [[ -e $2 ]] || return

    # را حفظ کند trap  به سختی تلاش می‌کند جانشین قبلی و وضعیتهای ‎
    # را تنظیم کند بازهم در زحمت است ERR یا DEBUG اما اگر فراخواننده 
    if [[ $- != *f* ]]; then
        set -f
        local oReturn=$(trap -p RETURN)
        trap 'set +f; trap "${oReturn:--}" RETURN' RETURN
    fi

    local line idx IFS=$'\n'
    for line in ${1:+$(<"$2")}; do
        printf -v "${1}[idx++]" %s "$line"
    done

 #:این یک جایگزین برای حلقه فوق، همان اندازه نامساعد، گرچه اندکی سریعتر
 # IFS=$'\n' declare -a ${1:+"$1"'=( $(<"$2") )'} 2>/dev/null
}
declare -ft evilReadLines # .را از فراخوانی کننده به ارث می‌برد trapها ‎

 # نامها و نام‌فایلها را به آرایه عبور می‌دهد
evilReadLines myArray myFile

2.2. خواندن جریانهای شامل جداکننده NUL

اگر با رکوردهایی سر و کار دارید که سطرهای جدید می‌توانند در آنها تعبیه شده باشد، شما در حال استفاده از یک جداکننده جایگزین از قبیل کاراکتر NUL‎ ( \0 )‎ برای جدا کردن رکوردها خواهید بود. در آن وضعیت، شما نیاز به استفاده ازشناسه ‎ -d‎ فرمان read نیز خواهید داشت:

# Bash
unset -v arr i
while IFS= read -rd '' 'arr[i++]'; do
    :
done < <(find . -name '*.ugly' -print0)

# or
while read -rd ''; do
    arr[i++]=$REPLY
done < <(find . -name '*.ugly' -print0)

# or (bash 3.1 and up)
while read -rd ''; do
    arr+=("$REPLY")
done < <(find . -name '*.ugly' -print0)

read -d ''‎ به Bash می‌گوید، تا رسیدن به یک بایت تهی به جای رسیدن به سطرجدید، خواندن را ادامه دهد. معلوم نیست این مطلب در تمام پوسته‌ها با ‎ -d‎ کار بکند.

2.3. الحاق به یک آرایه موجود

به طوری که قبلاً اشاره شد، آرایه‌ها پراکنده هستند - یعنی، تضمین نمی‌شود که شاخصهای عددی همجوار با کمیت‌ها اشغال شده باشند. این موضوع، آنچه به معنی «الحاق به یک آرایه موجود» می‌باشد را مغشوش می‌کند. چند رویکرد (برای الحاق)وجود دارد.

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

# Bash/ksh93
arr[++i]="new item"

اگر نمی‌خواهید یک متغیر شاخص نگهداری کنید، اما می‌دانید که آرایه شما پراکنده نیست، آنوقت می‌توانید از تعداد عناصر برای محاسبه مبدأ الحاق استفاده کنید(پیشنهاد نمی‌شود):

# Bash/ksh
# .اگر آرایه دارای حفره باشد(پرکنده باشد) ناموفق خواهد شد
arr[${#arr[@]}]="new item"

اگر نمی‌دانید آیا آرایه شما پراکنده است یا خیر، اما در نظر ندارید تمام آرایه را شاخص‌گذاری مجدد نمایید(بسیار کم بازده)، پس می‌توانید از این استفاده کنید:

# Bash
arr=("${arr[@]}" "new item")

# Ksh
set -A arr -- "${arr[@]}" "new item"

اگر در bash نگارش 3.1 یا بالاتر هستید، می‌توانید عملگر ‎ +=‎ را به کار ببرید:

# Bash 3.1, ksh93, mksh, zsh
arr+=(item 'another item')

توجه: پرانتزها لازم می‌باشند، درست مانند موقع تخصیص یک آرایه. در غیر آن صورت شما با الحاق به ‎ ${arr[0]}‎ که مترادف ‎$arr‎ است مواجه می‌شوید. اگر پوسته شما این نوع الحاق را پشتیبانی می‌کند، این روش ارجح می‌باشد.

برای نمونه‌هایی ازکاربرد آرایه جهت نگهداری دستورات پیچیده پوسته، پرسش و پاسخ شماره 50 و شماره 40 را ملاحظه کنید.

3. بازیافت کمیت‌ها از یک آرایه

${#arr[@]}‎ یا ‎${#arr[*]}‎ به تعداد عناصر آرایه بسط می‌یابند:

# Bash
shopt -s nullglob
oggs=(*.ogg)
echo "There are ${#oggs[@]} Ogg files."

عناصر منفرد به وسیله شاخص بازیابی می‌شوند:

echo "${foo[0]} - ${bar[j+1]}"

کروشه‌ها زمینه محاسبات می‌باشند. درون زمینه محاسباتی، به متغیرها، از جمله آرایه‌ها، توسط نام می‌تواند رجوع بشود. برای مثال، در بسط زیر:

${arr[x[3+arr[2]]]}

شاخص arr کمیت آن عضو آرایه x که شاخص آن 3 به اضافه مقدار‎ arr[2]‎ است، خواهد بود.

استفاده دسته جمعی از عناصر آرایه، یکی از ویژگیهای کلیدی آرایه‌های پوسته است. دقیقاً همانگونه که ‎ "$@"‎ به پارامترهای مکانی بسط می‌یابد، ‎ "${arr[@]}"‎ به لیستی از کلمات، هر عضو آرایه به یک کلمه، بسط می‌یابد. برای مثال:

# Korn/Bash
for x in "${arr[@]}"; do
  echo "next element is '$x'"
done

حتی اگر عناصر آرایه محتوی فضای سفید باشند این کُد کار می‌کند. همواره به همان تعداد کلمه منجر می‌شود که آرایه‌ای با آن تعداد عضو دارید.

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

# Bash/ksh
printf "%s\n" "${arr[@]}"

برای نسخه‌برداری اندکی پیچیده‌تر از آرایه،‎ "${arr[*]}"‎ باعث می‌شود عناصر با اولین کاراکتر متغیر IFS( یا در صورت برقرار نبودن متغیر IFS با یک فاصله) در میان آنها، به یکدیگر پیوسته شوند. در نتیجه وقوع آن، ‎"$*"‎ به همان روش پارامترهای مکانی بسط داده می‌شود.

# Bash
arr=(x y z)
IFS=/; echo "${arr[*]}"; unset IFS
# prints x/y/z

متأسفانه، نمی‌توانید کاراکترهای چندتایی را با کاربرد این روش مابین عناصر آرایه قرار بدهید. به جای آن باید چیزی مشابه این را به کار ببرید:

# Bash/ksh
arr=(x y z)
tmp=$(printf "%s<=>" "${arr[@]}")
echo "${tmp%<=>}"    # .اضافی را از انتها حذف می‌کند ‎<=>‎ 
# را چاپ می‌کند ‎x<=>y<=>z‎

یا از بُرش آرایه، که در بخش بعد شرح داده شده، استفاده کنید.

# Bash/ksh
typeset -a a=([0]=x [5]=y [10]=z)
printf '%s<=>' "${a[@]::${#a[@]}-1}"
printf '%s\n' "${a[@]:(-1)}"

این نیز نشان می‌دهد که چطور عناصر چندگانه آرایه‌های پراکنده می‌تواند در یک نوبت تخصیص داده شود. توجه نمایید استفاده از نشانه ‎ arr=([key]=value ...)‎ در میان پوسته‌ها متفاوت است. در ksh93، این ترکیب دستوری به طور پیش‌فرض به شما یک آرایه انجمنی می‌دهد مگر اینکه شما طور دیگری تعیین کنید، و کاربرد آن مستلزم تعیین کمیت یک شاخص معین به طور صریح است، برخلاف bash، که جایی که شاخص‌ها از قلم افتاده باشند از شاخص قبلی شروع می‌کند . این مثال به طریقی نوشته شده است که با هر دو سازگار باشد.

BASH نگارش 3.0 قابلیت بازیابی کمیت شاخص‌های یک آرایه را اضافه نموده:

# Bash 3.0 or higher
arr=(0 1 2 3) arr[42]='what was the question?'
unset 'arr[2]'
echo "${!arr[@]}"
# چاپ می‌کند 0 1 3 42 ‎
# (مترجم: درصورتیکه کد زیر محتوای عناصر آرایه را چاپ می‌کند نه کمیت خود شاخص‌ها را)
echo "${arr[@]}"
# را چاپ می‌کند‎  0 1 2 3  what was the question?  

بازیابی شاخص‌ها برای بعضی از انواع معین وظایف بینهایت اهمیت دارد، از قبیل نگهداری آرایه‌های موازی با شاخص‌های یکسان( روشی کم بها برای تقلید یک آرایه از structها در زبان فاقد struct است):

# Bash 3.0 or higher
unset file title artist i
for f in ./*.mp3; do
  file[i]=$f
  title[i]=$(mp3info -p %t "$f")
  artist[i++]=$(mp3info -p %a "$f")
done

#   بعداً، روی هر فایل صوتی تکرار می‌کند. حتی اگر آرایه پراکنده ‎
# . باشد، این روش کار می‌کند، فقط به شرط آنکه حفره‌های آنها یکسان باشد
for i in "${!file[@]}"; do
  echo "${file[i]} is ${title[i]} by ${artist[i]}"
done

3.1. بازیابی با اصلاحات

بسط‌های پارامتر Bash می‌تواند روی عناصر آرایه به طور دسته جمعی انجام بشود:

# Bash
arr=(abc def ghi jkl)
echo "${arr[@]#?}"          # را چاپ می‌کند bc ef hi kl
echo "${arr[@]/[aeiou]/}"   # را چاپ می‌کند bc df gh jkl

بسط پارامتر نیز می‌تواند برای استخراج عضوهای یک آرایه به کار برود. بعضی ها این را بُرش می‌نامند:

# Bash
echo "${arr[@]:1:3}"        # (دومین عنصر) ‎#1‎ سه عنصر با شروع ازعضو شماره 
echo "${arr[@]:(-2)}"       # دوعضو انتها‎
echo "${@:(-1)}"            # آخرین پارامتر مکانی‎
echo "${@:(-2):1}"          # پارامتر مکانی دوم از انتها

4. استفاده از @ همچون یک شِبه-آرایه

به طوری که در بالا دیدیم، آرایه @(آرایه‌ای از پارامترهای مکانی) می‌تواند تقریباً مانند آرایه نامگذاری شده عادی استفاده بشود. این تنها متغیر آرایه‌ای مورد استفاده در پوسته‌های POSIX یا Bourne می‌باشد. این آرایه محدودیت‌های معینی دارد : نمی‌توانید به طور جداگانه عناصر منفرد را برقرار یا حذف کنید، و نمی‌تواند پراکنده باشد. با وجود این، بازهم انجام برخی وظایف معین در پوسته POSIX را که در غیر آن صورت مستلزم ابزارهای خارجی بودند، امکان‌پذیر می‌سازد:

# POSIX
set -- *.mp3
if [ -e "$1" ]; then
  echo "there are $# MP3 files"
else
  echo "there are 0 MP3 files"
fi

# POSIX
...
# .یک گزینه به لیست گزینه‌های ما که به طور پویا تولید شده‌اند، اضافه می‌کند
set -- "$@" -f "$somefile"
...
foocommand "$@"

(با دستورات تولید شده به طور پویا در پرسش و پاسخ 50 با استفاده از آرایه‌های نام گذاری شده، مقایسه کنید.)

See Also


CategoryShell

پرسش و پاسخهای Bash -شماره 5 (آخرین ویرایش ‎ 2012-12-01 15:17:42 ‎ توسط 148)


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