توصیف‌گرهای فایل - آموزش اسکریپت نویسی
X
تبلیغات
رایتل

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

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

#!/bin/bash

توصیف‌گرهای فایل

FileDescriptor

توصیف‌گرهای فایل

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

هر پردازشی سه FD را از پدرش به ارث خواهد برد: 0 (ورودی استاندارد)، باز برای خواندن، و 1 (خروجی استاندارد)، و 2 (خطای استاندارد) باز برای نوشتن. پردازشی که بدون یکی از اینها یا بیشر، شروع بشود ممکن است به طور غیر قابل پیش‌بینی رفتار کند. (بنابراین هرگز stderr را نبندید. به جای آن همیشه به ‎/dev/null‎ هدایت نمایید.)

امکان دارد پردازشها چنانکه لازم باشد (تا هر اندازه‌ای که محدودیت آن را سیستم عامل تحمیل کند) توصیف‌گر فایل اضافه باز کنند. در اکثر زبانها، وقتی شما فایل جدیدی باز می‌کنید، شماره FD که سیستم عامل انتحاب می‌کند به شما مسترد می‌شود (یا یک کتابخانه شماره FD را مدیریت می‌کند و جزئیات را پنهان می‌سازد). اگر چه، در اسکریپت‌های پوسته، الگو متفاوت است: شما اول شماره FD را انتخاب می‌کنید، و سپس فایل را برای استفاده از آن توصیف‌گر، باز می‌کنید. این یعنی شما به عنوان نویسنده اسکریپت، باید از آن FDهایی که در حال استفاده برای هر وظیفه‌ای هستید، پیوسته آگاهی داشته باشید.

پوسته‌ها برای کار با توصیف‌گرهای‌فایل از تغییر مسیر استفاده می‌کنند. برای FDهای از قبل موجود، با استفاده از ترکیب دوتایی کردن توصیف‌گرفایل، خروجی می‌تواند، به آنها ارسال گردد, یا ورودی می‌تواند از آنها خوانده شود:

echo "unexpected error: $foo" 1>&2

while read -r line 0<&3; do ...

راهنمای Bash و پرسش و پاسخ شماره ۵۵ یک شرح مقدماتی به دست می‌دهند، بنابراین ما در اینجا خلاصه خواهیم نمود. در مثال echo، می‌دانیم که echo به طور طبیعی در ‎stdout (FD 1)‎ می‌نویسد بنابراین ما ‎ FD 1‎ را برای اشاره کردن به جایی که ‎FD 2‎ اشاره می‌کند، باطل می‌کنیم. به این ترتیب، echo برای نوشتن در ‎stderr (FD 2)‎ ما، فریب داده می‌شود. در مثال read، می‌دانیم که read به طور طبیعی از ‎stdin (FD 0)‎ می‌خواند، لیکن ما ‎FD 0‎ را برای اشاره کردن به جایی که ‎FD 3‎ ما اشاره می‌کند، لغو کرده‌ایم، و بنابراین read ورودی‌اش را به جای ‎FD 0‎ ما، از آنجا بیرون می‌کشد. این تغییر مسیرها زودگذر هستند، تنها بر دستورات منفردی که در آنجا ظاهر می‌گردند اِعمال می‌شوند.

برای ایجاد FDهای جدید، ما باید فایلهایی را برای اشاره کردن به آنها باز کنیم. به طور نمونه، ما می‌خواهیم FD در داخل پوسته ما در دسترس باشد، به طوری که بتواند استفاده مجدد بشود یا چنانکه لازم شود به فرزندان عبور داده شود. برای باز کردن یک فایل در اسکریپت پوسته، ما فرمان exec را به کار می‌بریم:

exec 3> myfifo

چنانکه قبلاً گفتیم، شما باید در زمانی که اسکریپت را می‌نویسید، بدانید که کدام شماره FDرا می‌خواهید برای هر وظیفه استفاده نمایید. در مثال فوق، ما ‎FD  3‎ را برای خروجی به یک فایل(یا یک چیزی) به نام myfifo باز می‌کنیم. احتمالاً ما بعداً از این توصیف‌گر فایل باز، برای نوشتن اطلاعات در فایل استفاده خواهیم نمود.

تغییر مسیر ورودی به همان طریق کار می‌کند:

exec 4< /etc/passwd

یک FD همچنین می‌تواند برای هر دو عمل خواندن و نوشتن باز بشود:

# Bash در پوسته‎
exec 3<> /dev/tcp/www.google.com/80

این مورد برای اکثر برنامه‌های کاربردی ورودی-خروجی سوکت ضروری است(ارسال یک پیغام به یک سرویس، و دریافت پاسخ از آن، روی همان سوکت منفرد).

وقتی یک FD باز شده است، میتواند با تکنیک‌های تغییر مسیری که قبلاً در این صفحه شرح‌داده شده است، برای خواندن و نوشتن به کار برود. این هم یک درخواست HTTP کامل در bash:

   1 #!/usr/bin/env bash
   2 exec 3<> /dev/tcp/www.google.com/80 || exit 1
   3 printf 'HEAD / HTTP/1.1\nHost: www.google.com\nConnection: close\n\n' >&3
   4 cat <&3

در اینجا، ‎FD 3‎ را برای اشاره کردن به سوکت TCP باز می‌کنیم، سپس یک درخواست HTTP در آن سوکت می‌نویسیم، بعد پاسخ را از سوکت می‌خوانیم.

کار با لوله‌های با نام معمولاً تکنیک‌های مشابهی را دربر می‌گیرد -- نخست ایجاد FIFO، سپس تنظیم یک خواننده و یک نویسنده. اگر نویسنده یک اسکریپت باشد، و مایل باشد بدون راه‌اندازی یک شرط EOF برای خواننده بیش از یکبار در FIFO بنویسد، آنوقت اسکریپت یک FD باز خواهد نمود:

exec 3> myfifo
echo "something" >&3
...
echo "something else" >&3

موقعی که کار ما با یک FD تمام شد، می‌توانیم آن را ببندیم. لازم است ما شماره آن را، و آنکه آیا برای خواندن یا نوشتن باز شده است را بدانیم.

exec 3>&-   # را که برای نوشتن باز شده بود، می‌بندد ‎FD 3‎ 
exec 4<&-   # را که برای خواندن باز شده بود می‌بندد ‎FD 4‎ 

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

Juggling FDs

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

در این مثال، ما پوسته را مجبور به باز کردن و مدیریت دو فایل مختلف در ‎FD 3‎ به طور هم زمان در یک پردازش منفرد، می‌کنیم. البته، داشتن دو فایل باز با هم در همان FD غیر ممکن است، باز هم طوری رفتار می‌کنند، مثل اینکه می‌توانید داشته باشید.

# ksh93 در پوسته‎
 ~ $ builtin cat fds; type cat
cat is a shell builtin
 ~ $ function f { fds -l; cat /dev/fd/3; }
 ~ $ { f 3<&4; f; } <<<33333 3<&0 <<<44444 4<&0 <&2
00 rw- crw--w---- /dev/pts/8
01 rw- crw--w---- /dev/pts/8
02 rw- crw--w---- /dev/pts/8
03 rw- -rw-r----- /dev/inode/22/53172158
04 rw- -rw-r----- /dev/inode/22/53172158
10 rwx -rw-r----- /dev/inode/22/53172157
44444
00 rw- crw--w---- /dev/pts/8
01 rw- crw--w---- /dev/pts/8
02 rw- crw--w---- /dev/pts/8
03 rw- -rw-r----- /dev/inode/22/53172157
04 rw- -rw-r----- /dev/inode/22/53172158
33333

دستور داخلی cat دعوت می‌شود برای اینکه فایل ویژه‌ای را تحت لینوکس باز کند که تضمین می‌شود به یک FD تعیین شده برای پردازش جاری اشاره کند، به طوری که پوسته نباید هیچ فریبکاری درونی به کار ببرد(چنان که با تغییر مسیرهای ‎</dev/fd/*‎ می‌توانست). به طوری که می‌توانید ببینید، پوسته این را با المثنی کردن FD3 به FD10 به دست می‌آورد، و به خاطر دارد که پس از برگشت از تابع، برگردد و FD موقتی را ببندد ( FD آزاد انتخابی پوسته که ‎>= 10‎ می‌باشد). ورود و خروج یک فرمان مرکب، تابع، یا دستور داخلی، واقعاً درست به واسطه این شکل ناخوش‌آیندتر و صریح‌تر، شیرین است:

$ { exec {savefd}<&3 3<&4; f; exec 3<&"$savefd"-; f; } <<<...

موقع انجام وظایف پیچیده،پایمال کردن این FDهای موقتی کاملاً ممکن است مخصوصاً، موقع دستکاری و خواندن به طور غیر مشخص از FDهای «شماره بالا»، وقتی که پوسته ممکن است در حال استفاده از آنها باشد. این یکی از دلایلی است که ksh93 و ‎Bash 4.1‎ دارایتغییر مسیرهای سبکِ ‎‎{var}‎هستند که به پوسته اجازه بدهد یک FD آزاد را برای شما تخصیص بدهد و مستقیماً آن را در متغیری قرار بدهد که بعد شما بتوانید به جای استفاده از شماره‌هایFDبه صورت ‎hard-coded‎ به آنها ارجاع کنید.


CategoryShell

توصیف‌گر فایل (آخرین ویرایش ‎2012-08-29 03:44:43‎ توسط ormaaj)