فصل ‎20‎- تغییر مسیر ورودی-خروجی

فهرست مطالب
‎20.1‎- کاربرد exec
‎20.2‎- تغییر مسیر بلوک‌های کد
‎20.3‎- کاربردها

همیشه سه فایل قراردادی‎[1]‎ باز وجود دارد، stdin (صفحه کلید)، stdout (صفحه نمایش)، و stderr (پیغام خطاهای خروجی روی نمایشگر). اینها، و هر فایل باز دیگر، می‌توانند تغییر مسیر داده شوند. تغییر مسیر فقط به معنی گرفتن خروجی از یک فایل، فرمان، برنامه، اسکریپت، یا حتی قطعه کدی در داخل یک اسکریپت (مثال ‎3-1‎ و مثال ‎3-2‎ را ببینید) و فرستادن آن به ورودی یک فایل، فرمان، برنامه، یا اسکریپت دیگر است.

هر فایل باز، یک توصیف‌گر فایل تخصیص یافته را کسب می‌کند. ‎[2]‎ توصیف‌گرهای فایل برای stdin‏، stdout‏، و stderr به ترتیب ‎0‎‏، ‎1‎‏، و ‎2‎ هستند. برای فایل‌های باز اضافی، توصیف‌گرهای ‎‎3‎ تا ‎9‎ وجود دارند. گاهی اوقات تخصیص یکی از این توصیف‌گرهای فایل اضافی به stdin‏، stdout‏، یا stderr به عنوان یک نسخه دوم موقت، مفید است. ‎[3]‎ این تخصیص، بازگشت به حالت عادی پس از تغییر مسیر پیچیده و تجدید سازمان را تسهیل می‌نماید (مثال ‎20-1‎ را ببینید).

‎COMMAND_OUTPUT >‎
تغییر مسیر ‎stdout‎ به یک فایل.
اگر فایل موجود نباشد آن را ایجاد می‌کند، وگرنه آن را رونویسی می‌نماید.
‎ls -lR > dir-tree.list‎
فایلی شامل یک لیست از درخت دایرکتوری ایجاد می‌کند.
‎: > filename‎
عملگر ‎>‎ فایل ‎"filename"‎ را به طول صفر کوتاه می‌کند.
اگر فایل موجود نباشد، فایل را با طول صفر ایجاد می‌کند (مانند اثر فرمانtouch).
کاراکتر : به عنوان یک جای‌نگهدار، بدون تولید هیچ خروجی عمل می‌کند.
‎> filename‎
عملگر ‎>‎ فایل «filename» را به طول صفر کوتاه می‌کند.
اگر فایل موجود نباشد، فایلی به طول صفر ایجاد می‌کند (مانند اثر فرمانtouch).
(مانند همان نتیجه «‎: ‎>‎‎» فوق، اما این با برخی پوسته‌ها کار نمی‌کند)
‎COMMAND_OUTPUT >>‎
‎stdout‎ را به یک فایل هدایت می‌کند.
اگر فایل موجود نباشد آن را ایجاد می‌کند، وگرنه به آن پیوست می‌نماید.

فرمان‌های تغییر مسیر یک سطری (فقط روی سطری که در آن هستند اثر می‌کنند):

--------------------------------------------------------------------
‎1>filename‎
‎stdout‎ را به فایل «filename» تغییر مسیر می‌دهد.
‎1>>filename‎
‎stdout‎ را به فایل «filename» تغییر مسیر داده و به انتهای آن اضافه می‌کند.
‎2>filename‎
‎stderr‎ را به فایل «filename» هدایت می‌کند.
‎2>>filename‎
‎stderr‎ را به فایل «filename» تغییر مسیر داده و به انتهای آن اضافه می‌کند.
‎&>filename‎
هر دو ‎stdout‎ و ‎stderr‎ را به فایل «filename» تغییر مسیر می‌دهد.
این عملگر اکنون از ‎Bash 4‎، نگارش نهایی، کار می‌کند.
‎M>N‎
‎M‎ یک توصیف‌گر فایل است، که اگر به طور صریح تنظیم نشود به طور قراردادی ‎1‎ می‌شود.
‎N‎ یک نام فایل است.
توصیف‌گر فایل M به فایل N هدایت می‌شود.
‎M>&N‎
M یک توصیف‌گر فایل است، که اگر تنظیم نشود به طور پیش‌فرض ‎1‎ است.
N یک توصیف‌گر فایل دیگر است.

تغییر مسیر ‎stdout‎ به صورت یک سطر در هر نوبت.

  LOGFILE=script.log
  echo "This statement is sent to the log file, \"$LOGFILE\"." 1>$LOGFILE
  echo "This statement is appended to \"$LOGFILE\"." 1>>$LOGFILE
  echo "This statement is also appended to \"$LOGFILE\"." 1>>$LOGFILE
  echo "This statement is echoed to stdout, and will not appear in \"$LOGFILE\"."
  # این فرمان‌های تغییر مسیر به طور خودکار پس از هر سطر ‎reset‎ می‌شوند.

تغییر مسیر ‎stderr‎ به صورت یک سطر در هر نوبت.

  ERRORFILE=script.errors
  bad_command1 2>$ERRORFILE       #پیغام خطا به ‎$ERRORFILE‎ فرستاده می‌شود.
  bad_command2 2>>$ERRORFILE      # پیغام خطا به ‎$ERRORFILE‎ پیوست می‌گردد.
  bad_command3                    #     پیغام خطا در ‎stderr‎ منعکس می‌شود و
                                  #+           در ‎$ERRORFILE‎ ظاهر نمی‌شود.
  #       این فرمان‌های تغییر مسیر به طور خودکار پس از هر سطر ‎reset‎ می‌شوند.

‎2>&1‎
‎stderr‎ را به ‎stdout‎ تغییر مسیر می‌دهد.
پیغام‌های خطا به همان محلی فرستاده می‌شوند که خروجی استاندارد می‌رود.
‎>>filename 2>&1‎
‎bad_command >>filename 2>&1‎
هردو ‎stdout‎ و ‎stderr‎ را به فایل «filename» پیوست می‌کند ...
‎2>&1 | [command(s)]‎
‎bad_command 2>&1 | awk '{print $5}'‎
‎stderr‎ را از طریق یک لوله می‌فرستد.
‎|&‎ به عنوان یک اختصار برای «‎2>&1 |‎» به ‎Bash 4‎ اضافه گردید.
‎i>&j‎
توصیف‌گر فایل i را به توصیف‌گر فایل j تغییر مسیر می‌دهد.
تمام خروجی فایل مورد اشاره به وسیله i به فایلی فرستاده می‌شوند که توسط j به آن اشاره می‌شود.
‎>&j‎
به طور پیش‌فرض، توصیف‌گر فایل ‎1‎ ‏(stdout)‏ را به توصیف‌گر فایل j تغییر مسیر می‌دهد.
تمام خروجی به فایلی فرستاده می‌شود که به وسیله j به آن اشاره می‌شود.

‎0< FILENAME‎
‎< FILENAME‎
پذیرفتن ورودی از یک فایل.
‎grep search-word <filename‎
فرمان هم‌نشین با ‎">"‎، و بیشتر اوقات در ترکیب با آن به کار می‌رود.
‎[j]<>filename‎
باز کردن فایل «filename» برای خواندن و نوشتن، و تخصیص توصیف‌گر فایل j به آن.
اگر «filename» موجود نباشد، آن را ایجاد می‌کند.
اگر توصیف‌گر فایل j مشخص نشده باشد،توصیف‌گر ‎0‎ یا ‎stdin‎ را پیش‌فرض می‌داند.

یک کاربرد آن، نوشتن در یک محل تعیین شده در یک فایل است.

      echo 1234567890 > File    #             نوشتن رشته در «File».
      exec 3<> File             #بازکردن «File» و تخصیص ‎fd 3‎ به آن.
      read -n 4 <&3             #             خواندن فقط ‎4‎ کاراکتر.
      echo -n . >&3             #      نوشتن یک نقطه اعشار در آنجا.
      exec 3>&-                 #                        بستن ‎fd 3‎.
      cat File                  #   ==> 1234.67890
                                #     چه جالب!!! دستیابی غیر ترتیبی.
|
لوله.
ابزار پردازش چند منظوره و زنجیر کردن فرمان‌ها.
مشابه با «‎>‎»، اما با عملکرد کلی‌تر.
مناسب برای زنجیر کردن فرمان‌ها، اسکریپت‌ها، فایل‌ها، و برنامه‌ها با یکدیگر.
‎cat *.txt | sort | uniq > result-file‎
خروجی تمام فایل‌های ‎.txt‎ را مرتب کرده و سطرهای تکراری را حدف می‌کند،
در نهایت نتایح را در «result-file» ذخیره می‌کند.

چندین نمونه از تغییر مسیر ورودی و خروجی و-یا لوله‌ می‌تواند در یک سطر فرمان واحد ترکیب گردد.

command < input-file > output-file
#  یا معادل:
< input-file command > output-file   #هر چند که این غیر استاندارد است.

command1 | command2 | command3 > output-file
مثال ‎16-31‎ و مثال ‎A-14‎ را ببینید.

ممکن است چند جریان خروجی به یک فایل تغییر مسیر داده شوند.

‎ls -yz >> command.log 2>&1‎
نتیجه گزینه‌های غیرمجاز yz در فایل «command.log» ضبط می‌شود.
چونکه stderr به فایل تغییر مسیر داده شده، هر پیغام خطایی هم به آنجا خواهد رفت.

اما، توجه نمایید که مورد زیر همان نتیجه را ارایه نمی‌کند.

‎ls -yz 2>&1 >> command.log‎
یک پیغام خطا بیرون می‌دهد، اما در فایل نوشته نمی‌شود.
به طور دقیق‌تر، خروجی فرمان را (در این حالت، هیچ) در فایل می‌نویسد، اما فقط پیغام خطا به stdout می‌رود(مترجم: چون قبل از اینکه stdout به فایل تغییر مسیر داده شود، stderr به آن تغییر مسیر داده شده.).
در حالت تغییر مسیر دادن هر دو مورد stdout و stderr، ترتیب فرمان‌ها یک اختلاف ایجاد می‌کند.

بستن توصیف‌گرهای فایل

‎n<&-‎

بستن توصیف‌گر فایل ورودی n.

‎0<&-‎‏، ‎<&-‎

بستن stdin.

‎n>&-‎

بستن توصیف‌گر فایل خروجی n.

‎1>&-‎‏، ‎>&-‎

بستن stdout.

پردازش‌های فرزند، توصیف‌گرهای فایل باز را به ارث می‌برند. بدین علت لوله‌ها کار می‌کند. برای پیش‌گیری از به ارث رسیدن یک توصیف‌گر فایل، آن توصیف‌گر را ببندید.

# تغییر مسیر دادن ‎stderr‎ تنها، به یک لوله.
exec 3>&1                              #               ذخیره مقدار فعلی ‎stdout‎
ls -l 2>&1 >&3 3>&- | grep bad         # بستن ‎fd 3‎ برای ‎grep‎ (امابرای ‎ls‎ خیر).
#              ^^^^   ^^^^
exec 3>&-                              # اکنون بستن آن برای باقیمانده اسکریپت.

# با تشکر از ‎S.C.‎

برای معرفی مفصل‌تر تغییر مسیر ورودی-خروجی پیوست F را ببینید.

یادداشت‌ها

‎[1]‎

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

‎[2]‎

یک توصیف‌گر فایل فقط یک عدد است که سیستم‌عامل به یک فایل باز برای دیده‌بانی آن تخصیص می‌دهد. آن را همچون نوع ساده شده اشاره‌گر فایل در نظر بگیرید. مشابه ‎file handle‎ در C است.

‎[3]‎

به کار بردن توصیف‌گر فایل ‎5‎ می‌تواند باعث مشکل بشود. وقتی Bash یک پردازش فرزند تولید می‌کند، به عنوان مثال با exec، فرزند ‎fd 5‎ را به ارث می‌برد (ایمیل آرشیو شده ‎Chet Ramey‎‏، SUBJECT: RE: File descriptor 5 is held open را ببینید). بهتر است این ‎fd‎ خاص را رها کنید.