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

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

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

#!/bin/bash

جایگزینی پردازش

ProcessSubstitution

جایگزینی پردازش

جایگزینی پردازش یک توسعه بسیار سودمند BASH است. مشابه ‎"command" | getline‎ در awk می‌باشد و بخصوص برای کنار گذاشتن پوسته‌های فرعی معلول خط‌لوله‌ها اهمیت دارد.

جایگزینی پردازش در دو شکل ظاهر می‌شود: ‎<(some command)‎ و ‎>(some command)‎. هر کدام نسبت به سیستم عامل، باعث می‌شوند یا یک FIFO در ‎/tmp‎ یا ‎/var/tmp‎ ایجاد بشود، یا یک دستگاه ویژه توصیف‌گر فایل (‎/dev/fd/*‎)، استفاده بشود. ترکیب دستوری جایگزینی توسط نام FIFO یا FD تعویض می‌گردد، وفرمان داخل آن در پس‌زمینه اجرا می‌گردد. جایگزینی در همان مرحله بسط پارامتر و جایگزینی فرمان انجام می‌شود.

یکی از رایج‌ترین موارد استفاده این ویژگی برای پرهیز از ایجاد فایلهای موقتی است، به عنوان مثال، موقع کاربرد ‎diff(1)‎:

diff <(sort list1) <(sort list2)

این مورد (تقریباً) معادل است با:

mkfifo /var/tmp/fifo1
mkfifo /var/tmp/fifo2
sort list1 >/var/tmp/fifo1 &
sort list2 >/var/tmp/fifo2 &
diff /var/tmp/fifo1 /var/tmp/fifo2
rm /var/tmp/fifo1 /var/tmp/fifo2

توجه نمایید که فرمان diff در حقیقت دو شناسه نام فایل دریافت می‌کند.

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

# اجرا گردد ksh88/ksh93 این مثال شکست می‌خورد مگر اینکه در ‎
i=0
sort list1 | while read line; do
  i=$(($i + 1))
  ...
done
echo "$i lines processed" # Always prints 0

اما این کار می‌کند:

# bash مثال کارآمد، با استفاده از ترکیب‎
i=0
while read line; do
  ((i++))
  ...
done < <(sort list1)
echo "$i lines processed"

تفاوت میان ‎<(...)‎ و ‎>(...)‎ صرفاً در روشی است که تغییر مسیر انجام می‌شود. با شکل ‎<(...)خواندن از جایگزینی انتظار می‌رود، و فرمان طوری تنظیم می‌گردد که آن را به عنوان خروجی استاندارش استفاده کند(مترجم:در آن بنویسد تا ما از آن بخوانیم). با ‎>(...)نوشتن در جایگزینی فرمان مورد انتظار است، و فرمان طوری تنظیم می‌گردد که از آن به عنوان ورودی استانداردش استفاده نماید(مترجم: که آنچه در آن نوشته می‌شود را از آن بخواند).

>(...)‎ موارد کمی به کار می‌رود، رایج‌ترین حالت آن در ترکیب عطفی با ‎tee(1)‎ می‌باشد.

exec > >(tee logfile)

# مابقی اسکریپت به اینجا روانه می‌شود‎
# .می‌شود، و به خروجی استاندارد واقعی نیز نمی‌رسد log خروجی استاندارد هر چیزی ‎
# نیز هستید(به عنوان مثال stderr مراقب مسائل بافر کردن باشید، مخصوصاً اگر دارای ‎
# .(اعلان برای ورودی کاربر ممکن است جلوی سطر قبلی خروجی استاندارد ظاهر شود

برای گفتگوی بیشتر در مورد کاربرد آن پرسش و پاسخ شماره ۱۰۶ را ببینید.

این هم یک مثال پیچیده‌تر:

hasFile='Note: the (top-|highly )?secret plans are backed up at:(.*)'
criticalFile=
while IFS= read -r line; do
    [[ $line ]] || continue
    case $line in
        '!!! '*)
            errMsg "${line#'!!! '}" ;;
        *important* )
            echo "$line" ;;
        * )
            if [[ $line =~ $hasFile ]]; then
                criticalFile=${BASH_REMATCH[2]}
                warn "File at $criticalFile"
            else
                spin
            fi
            ;;
    esac
done < <(someCommand "${options[@]}" "${param[@]}" 2>&1 | tee "$logfile")
[[ $criticalFile ]] || abort 'File not found'

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

در مثال فوق regex به آسانی می‌توانست با یک case اجرا بشود:

  'Note: the '*'secret plans are backed up at:'*) criticalFile=${line#*'secret plans are backed up at:'}

جایگزینی پردازش مخصوصاً در جایی که بخش بیرونی یک فرمان awk می‌باشد، قدرتمند و قابل انعطاف است.

قابلیت حمل

Bash و Zsh و and ‎AT&T ksh{88,93}‎ (اما ‎pdksh/mksh‎ خیر) جایگزینی پردازش را پشتیبانی می‌کنند. جایگزینی پردازش توسط POSIX تعریف نشده. می‌توانید لوله‌های با نام را برای انجام همان موارد به کار ببرید. کم پردازشها نیز قادر به انجام هر کاری هستند که جایگزینی‌های پردازش می‌توانند، و کمی قابل حمل‌تر هستند(اگرچه ترکیب دستوری برای کاربرد آنها اینطور نیست).

تله‌ها

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

commandA <(commandB; [commandB's exit code is available here from $?])
[commandB's exit code cannot be obtained from here.  $? holds commandA's exit code]

اگر شما کد خروج در اسکریپت اصلی را لازم دارید، نیاز خواهید داشت کدتان را بازنویسی کنید و commandB را از جایگزینی پردازش بیرون ببرید. بسته به مسئله واقعی شما این گزینه‌ها ممکن است برای شما قابل بررسی باشد:

# بخواند stdin بتواند داده‌ها را از commandA اگر ‎ 
commandB | commandA     # به دست بیاورید. PIPESTATUS را از متغیر commandB شما می‌توانید کد خروج‎
commandB > >(commandA)
#   (if به دست آورید(یا با گذاشتن این در یک ‎$?‎ را از commandB اکنون می‌توانید کد خروج‎

#            بخواند، اما یک شناسه فایل لازم دارد stdin نمی‌تواند آن را از commandA اگر‎
commandB > >(commandA <(cat))       # قابل دستیابی است ‎$?‎ از commandB دوباره کد خروج‎
#  ،را در حافظه نیز نگهداری کنید. وقتی اینکار را انجام بدهید commandB می‌توانید خروجی ‎
# قرار بدهید if به دست بیاورید یا تخصیص را در یک ‎$?‎ را از commandB  می‌توانید کد خروج ‎
b=$(commandB); commandA <<< "$b"    # می‌خواند stdin را از commandB خروجی commandA اینجا‎
b=$(commandB); commandA <(cat "$b") # را از یک شناسه فایل دریافت می‌کند commandB خروجی commandA اینجا‎

See Also


CategoryShell

جایگزینی پردازش (آخرین ویرایش ‎2013-01-14 20:01:03‎ توسط ormaaj)