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

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

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

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

دستکاری توصیف‌گر فایل

ادامه یادداشت قبل


4.2. دستکاری توصیف‌گر فایل

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

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

    $ echo "I am a proud sentence." > file

فایلی به نام file ساخته‌ایم، و یک جمله در آن نوشته‌ایم.

برنامه کاربردی به نام grep وجود دارد که به طور مختصر در فصل قبل دیده‌ایم. grep شبیه یک نوار لوله‌ای است: می‌توانید تقریباً در هر پروژه‌ای آن را به کار ببرید(خواه ایده خوبی باشد یا نباشد). اساساً یک الگوی جستجو را به عنوان یک شناسه دریافت می‌کند و می‌تواند چند نام فایل نیز به عنوان شناسه اضافی دریافت نماید. درست مانند cat، برنامه grep نیز اگر نام فایلی را به عنوان شناسه تعیین نکنید از stdin استفاده می‌کند. grep فایلها رامی‌خواند(یا stdin را در صورتکه فایلی مشخص نشده باشد) و الگوی جستجویی که به آن داده‌اید را جستجو می‌کند. اکثر نگارش‌های grep حتی از یک گزینه ‎-r‎ نیز پشتیبانی می‌کنند، که موجب می‌گردد دایرکتوریها را نیز مانند فایلها به عنوان شناسه اضافی قبول کنند، و سپس تمام فایلها و دایرکتوری‌های داخل آن شاخه‌ها را که به آن داده‌اید، جستجو می‌کند. در اینجا مثالی برای آن که grep چگونه می‌تواند عمل کند می‌آوریم:

    $ ls house/
    drawer  closet  dustbin  sofa
    $ grep -r socks house/
    house/sofa:socks

در این مثال تخیلی، دایرکتوری به نام house با چند قطعه اثاثیه منزل در آن به عنوان فایل داریم. اگر ما در جستجوی socks(ساک‌ها)ی خود در هر یک از آن فایلها باشیم، grep را به جستجو در دایرکتوری ‎house/‎ می‌فرستیم. grep هر چیزی در آنجا را جستجو می‌کند، هر فایل را باز می‌کند و محتویات آن را نگاه می‌کند. در مثال ما، grep ساک‌ها( socks) را در فایل ‎house/sofa‎ پیدا می‌کند،احتمالاً پنهان شده زیر یک بالش. مثال واقع‌بینانه‌تری می‌خواهید؟ حتماً:

    $ grep "$HOSTNAME" /etc/*
    /etc/hosts:127.0.0.1       localhost Lyndir

اینجا به grep دستور داده‌ایم هر آنچه ‎$HOSTNAME‎ به آن بسط می‌یابد را در هر فایلی که ‎ /etc/*‎ به آن بسط می‌یابد، جستجو نماید. نام میزبانم را که Lyndir است در فایل ‎/etc/hosts‎ پیدا می‌کند، و سطری از آن فایل را که شامل الگوی جستجو است، به من نشان می‌دهد.

خوب، حال که grep را دریافتید, اجازه بدهید با دستکاری توصیف‌گر‌فایل ادامه دهیم. به خاطر می‌آورید که فایلی به نام file ایجاد کردیم، و یک جمله شامل proud در آن نوشتیم؟ اینک اجازه بدهید grep را برای یافتن جمله proud به کار ببریم:

    $ grep proud *
    file:I am a proud sentence.

صحیح! grep جمله ما را در file یافته است. نتیجه عمل خود را در stdout که در ترمینال ما نمایش داده می‌شود، می‌نویسد. حالا بیایید ببینیم آیا می‌نوانیم با grep یک پیغام خطا نیز ارسال کنیم:

    $ grep proud file 'not a file'
    file:I am a proud sentence.
    grep: not a file: No such file or directory

این دفعه، به grep دستور داده‌ایم رشته proud را درفایلهای 'file' و 'not a file' جستجو کند. file وجود دارد، و جمله در آن هست، بنابراین grep با خوشحالی نتیجه را در stdout می‌نویسد. برای بررسی سراغ فایل بعدی که 'not a file' است، می‌رود. grep نمی‌تواند چنین فایلی را برای خواندن باز کند، به دلیل آنکه وجود ندارد. در نتیجه، grep یک پیغام خطا به stderr که بازهم به ترمینال ما متصل است، ارسال می‌کند.

حال، چطور می‌توان این جمله grep را به طور کامل خاموش کنیم؟ می‌توانیم تمام خروجی که روی ترمینال ظاهر می‌شود را به جای آن به یک فایل بفرستیم، اجازه بدهید آن را proud.log بنامیم:

    # Not quite right....
    $ grep proud file 'not a file' > proud.log 2> proud.log

صحیح به نظر می‌آید؟ ابتدا از ‎ >‎ برای ارسال stdout به فایل proud.log استفاده کرده‌ایم، و سپس از ‎ 2>‎ هم برای ارسال stderr به فایل proud.log. تقریبا درست، اما نه کاملاً. اگر این دستور را اجرا نمایید وسپس به فایل proud.log نگاه کنید، (لااقل در برخی سیستم‌ها) فقط یک پیغام خطا خواهید دید، نه خروجی مربوط به stdout را. در اینجا ما شرایط خیلی نامناسبی ایجاد نموده‌ایم. دو FD ایجاد کرده‌ایم که هر دو به طور مستقل از یکدیگر به یک فایل اشاره می‌کنند. نتایج آن به طور کامل معلوم نیست. بر اساس چگونگی مدیریت توصیف‌گرهای فایل توسط سیستم‌عامل شما، ممکن است بعضاً اطلاعات نوشته شده از طریق یک FD اطلاعات نوشته شده از طریق FD دیگر را پاک کنند.

    $ echo "I am a very proud sentence with a lot of words in it, all for you." > file2
    $ grep proud file2 'not a file' > proud.log 2> proud.log
    $ cat proud.log
    grep: not a file: No such file or directory
    of words in it, all for you.

در اینجا چه اتفاقی رخ داده؟ grep اول فایل file2 را باز کرده، آنچه را به او گفته‌ایم جستجو کند، یافته و سپس جمله very proud ما را در stdout(یعنیFD شماره 1) نوشته است. FD شماره 1 به فایل proud.log اشاره می‌کند، بنابراین اطلاعات در این فایل نوشته شده است. اما، توصیف‌گر‌فایل دیگری هم (FD شماره 2) داریم که به همین فایل و بویژه به ابتدای این فایل اشاره می‌کند. وقتی grep سعی می‌کند فایل 'not a file' را برای خواندن باز کند، نمی‌تواند. پس، یک پیغام خطا در stderr یعنی(FD شماره 2) می‌نویسد، که به ابتدای فایل proud.log اشاره می‌کرد. در نتیجه، دومین عمل نوشتن، اطلاعات اولی را رونویسی می‌کند!

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

    $ grep proud file 'not a file' > proud.log 2>&1

برای درک این مطالب لازم است توجه داشته باشید که: تغییر مسیر فایل از چپ به راست خوانده می‌شود.این ترتیبی است که BASH آنها را پردازش می‌کند. اول، stdout طوری تغییر داده می‌شود که به فایل proud.log اشاره کند. بعد، ما از ترکیب ‎ >&‎ برای دو نسخه‌ای نمودن FD شماره 1 و قرار دادن آن نسخه دوم در FD شماره 2 استفاده کرده‌ایم.

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

مراقب باشید ترتیب آنها را اشتباه نکنید:

    $ grep proud file 'not a file' 2>&1 > proud.log

این مثال stderr را نسبت به جایی که stdout اشاره می‌کند(که ترمینال است) دونسخه‌ای می‌کند، و بعد stdout به فایل proud.log تغییر مسیر داده خواهد شد. در نتیجه پیغام‌های stdout ثبت می‌شوند، اما پیغام خطاها بازهم به ترمینال می‌روند. ای وای.

نکته: برای راحتی، BASH همچنین شکل دیگری از تغییر مسیر را در دسترس شما قرار می‌دهد. عملگر تغییر مسیر ‎&>‎ در حقیقت شکل کوتاه شده‌ای از آنچه ما در اینجا دیدیم، است، تغییر مسیر stdout و stderr هر دو به یک فایل:

    $ grep proud file 'not a file' &> proud.log

این نیز همان ‎proud.log  2>&1‎ می‌باشد، اما قابل حمل به پوسته BourneShell نیست. این تکنیک پیشنهاد شده‌ای نیست، اما اگر ببینید که شخص دیگری از آن در اسکریپت خود استفاده کرده، باید آن را تشخیص بدهید.




ادامه دارد...

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