تمام پردازشهای Bash به ترتیب از چپ به راست تغییر مسیر داده میشوند. و ترتیب معنا دار است. انحراف از آن در یک فرمان ممکن است نتایج آن فرمان را تغییر بدهد.
اگر تمام آنچه میخواهید ارسال هر دو خروجی استاندارد و خطای استاندارد به یک فایل است، این مورد را به کار ببرید:
# Bourne foo >file 2>&1 # هر دو را به فایل ارسال میکند stdout و stderr
این هم یک نمایش ساده از آنچه اتفاق میافتد:
# POSIX foo() { echo "This is stdout" echo "This is stderr" 1>&2 } foo >/dev/null 2>&1 # هیج خروجی ارائه نمیکند foo 2>&1 >/dev/null # "This is stderr" در نمایشگر مینویسد
چرا نتایج اختلاف دارند؟ در حالت اول،>/dev/null ابتدا انجام میشود، و بنابراین، خروجی استاندارد فرمان به /dev/null فرستاده میشود. سپس، 2>&1 انجام میشود، که باعث میشود استاندارد خطا به همان جایی ارسال شود که خروجی استاندارد از قبل میرفت. بنابراین هر دو خروجی، دور انداخته میشوند.
در مثال دوم، 2>&1 اول انجام میشود. این به معنای آن است که ابتدا خطای استاندارد به جایی ارسال میشود که خروجی استاندارد به آنجا میرود --در این حالت، به ترمینال کاربر. پس از آن، خروجی استاندارد به /dev/null فرستاده میشود و بنابراین دور انداخته میشود. از آن جهت موقعی که ما foo را دفعه دوم اجرا میکنیم، فقط خطای استانداردش را میبینیم، نه خروجی استاندارد آن را.
فصل تغییر مسیر در راهنما شرح میدهد که چرا ما دونسخهای نمودن توصیفگر فایل را به جای دوبار باز کردن /dev/null به کار میبریم. در حالت خاصِ /dev/null واقعاً مسئلهای نیست زیرا تمام نوشتهها رها میشوند، اما موقعی که ما در فایل ثبت وقایع مینویسیم، به راستی خیلی زیاد با اهمیت است.
مواقعی هست که ما به راستی میخواهیم 2>&1 اول ظاهر شود -- برای یک مثال از این مورد، پرسش و پاسخ شماره 40 را ببینید.
مواقع دیگری هست که شاید از 2>&1 بدون هر تغییر مسیر دیگری استفاده کنیم. ملاحظه نمایید:
# Bourne find ... 2>&1 | grep "some error"
در این مثال، میخواهیم خطای استاندارد find را (به علاوه خروجی استاندارد آن)برای رشته "some error" جستجو کنیم. 2>&1 در فرمان لولهکشی شده خطای استاندارد را مجبور میکند همراه با خروجی استاندارد به داخل لوله برود. (موقعی که لولهها و تغییر مسیرها به این طریق مختلط میشوند، به خاطر بیاورید: لوله اول قبل از هر تغییر مسیری انجام میشود. بنابراین خروجی استاندارد find قبلاً برای اشاره کردن به لوله تنظیم گردیدهاست، پیش از اینکه ما تغییر مسیر 2>&1 را پردازش کنیم.)
اگر ما میخواستیم فقط خطای استاندارد را در لوله بخوانیم، و خروجی استاندارد را رها کنیم، میتوانستیم چنین کنیم:
# Bourne find ... 2>&1 >/dev/null | grep "some error"
تغییر مسیرها در این مثال بدین گونه پردازش میشوند:
اول، لوله ایجاد میشود. خروجی find به آن ارسال میشود.
بعد، 2>&1 باعث میشود خطای استاندارد find نیز به لوله برود.
عاقبت، >/dev/null موجب میگردد خروجی استاندارد findدور انداخته شود، فقط خطای استاندارد اجازه رفتن به لوله دارد.
یک پرسش مرتبط پرسش و پاسخ شماره 47 است، که چگونگی ارسال stderr به خط لوله را بحث میکند.
برای یک توضیح بیشتر گرافیکی قابل درک نمودن عملگر کپی توصیفگر فایل را ببینید.
اگر هنوز در مورد این نکته سر در گم هستید، احتمال دارد به علت آن باشد که شما با تصور غلطی در مورد چگونگی کار توصیفگرهای فایل آغاز نمودهاید, و هنوز نمیتوانید آن تصور غلط را رها کنید. نگران نشوید -- این یک موردِ به شدت رایجِ تصور غلط است، و شما تنها نیستید. اجازه بدهید من سعی در تشریح آن بنمایم....
بسیاری اشخاص تصور میکنند که 2>&1 یکجور «به هم پیوستن» یا «گره زدن با هم» یا « ازدواج کردن» دو توصیفگر فایل است، به طوریکه هر تغییر در یکی از آنها تغییری برای دیگری میشود. این حالت نیست. و برای بسیاری افراد، این همان جایی است که گیج شدن از آن حاصل میشود.
2>&1 فقط FD2 را تغییر میدهد تا به "آن جایی که FD1 در آن لحظه به آن اشاره میکند" اشاره نماید، در واقع باعث نمیشود که FD2 به خود FD1 اشاره کند. توجه نمایید که "2" و "1" به سبب روشی که به کار بروند، معانی مختلفی میگیرند: "2"، وقتی قبل از ">&" واقع میشود FD2 واقعی معنی میدهد، اما "1" که بعد از ">&" واقع میشود، به جای خود FD1، به معنی «جایی که FD1 در حال حاضر به آن اشاره میکند» است. (اگر برعکس باشد، همچون "1>&2"، آنوقت 1به معنی خود FD1 میشود، و 2 به معنی «جایی که FD2 در حال حاضر به آن اشاره میکند» خواهد بود.)
شاید قیاسها کمک کنند. یک قیاس در نظر گرفتن FDها به عنوان اشارهگرهای C است.
int some_actual_integer; int *fd1, *fd2; fd1 = &some_actual_integer; /* 1>file قابل مقایسه با*/ fd2 = fd1; /* 2>&1 قابل مقایسه با*/ fd1 = NULL; /* 1>&- قابل مقایسه با*/ /* هنوز به مکان واقعی حافظه اشاره میکند fd2 در این نقطه هر دو باید به یک نقطه اشاره کنند fd2 و fd1 این واقعیت که مناسب نیست. میتوانیم یکی از آنها را ببندیم یا دوباره باز کنیم بدون تأثیر بر دیگری */
یک قیاس دیگر در نظر گرفتن آنها مانند hardlinkها در سیستم فایل است .
touch some_real_file ln some_real_file fd1 # یک لینک به فایل ما ایجاد میکند fd1 ln fd1 fd2 # لینک دیگری به فایل ما ایجاد میکند fd2 rm fd1 # تحت تأثیر قرار نمیگیرد fd2 حذف میشود اما لینک fd1 لینک # "some_real_file":در این نقطه ما بازهم یک فایل با دو لینک داریم # "fd2"و
یا مانند پیوندهای نمادین -- اما با این قیاس باید محتاط باشیم.
touch some_real_file ln -s some_real_file fd1 # را پیوند نمادین از فایل ما میسازد fd1 ln -s "$(readlink fd1)" fd2 # را به همان فایلی پیوند نمادین میسازد که fd2 # .به آن لینک نمادین است fd1 rm fd1 # .دست نخورده میماند fd2 را حذف میکند اما fd1 # .را لازم دارد "readlink" برنامه غیر استاندارد # :نتیجه عبارت است از lrwxrwxrwx 1 wooledg wooledg 14 Mar 25 09:19 fd2 -> some_real_file -rw-r--r-- 1 wooledg wooledg 0 Mar 25 09:19 some_real_file # را استفاده کنیم، به طور "ln -s fd1 fd2" اگر ما سعی میکردیم در این مقایسه # ها نیست، بلکه چگونگی تصورFD نامساعدی شکست میخوردیم. این چگونگی کارکرد # .برخی افراد در باره کار آنها میباشد. و این اشتباه است
مقایسههای دیگر، از جمله تخیل FDها به منزله شیلنگها. با در نظر گرفتن فایلها به عنوان بشکههای پُر آب(یا خالی، یا نیمه پُر). شما میتوانید شیلنگ را در بشکه قرار بدهید، برای آنکه آب بیشتری در آن انباشته شود. میتوانید دو شیلنگ را در یک بشکه قرار بدهید، و آنها هر دو آب را در همان بشکه میریزند. آنوقت شما میتوانید یکی از آن شیلنگها را حذف کنید، و این امر موجب نمیشود شیلنگ دیگر کنار برود. آن یکی بازهم آنجاست.
پرسش و پاسخ 55 (آخرین ویرایش 2011-08-23 17:34:06 توسط S010600032d00065e)