مقدمه

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

اینجا جایی است که عملگر Pipe یا «پایپ» با نماد | وارد میدان می‌شود. پایپ یکی از قدرتمندترین و بنیادی‌ترین مفاهیم در فلسفه یونیکس و لینوکس است. این عملگر به ما اجازه می‌دهد تا خروجی استاندارد (stdout) یک دستور را مستقیماً به ورودی استاندارد (stdin) یک دستور دیگر متصل کنیم. با استفاده از پایپ، می‌توانیم زنجیره‌ای از دستورات ساده بسازیم که با همکاری یکدیگر، وظایف بسیار پیچیده‌ای را انجام می‌دهند.

عملگر Pipe چگونه کار می‌کند؟

نحوه‌ی کار پایپ بسیار ساده و هوشمندانه است. ساختار کلی آن به شکل command1 | command2 است. اتفاقی که می‌افتد به این صورت است:

  1. شل هر دو دستور command1 و command2 را تقریباً همزمان اجرا می‌کند.
  2. اما به جای اینکه خروجی استاندارد command1 به صفحه نمایش برود، شل آن را به ورودی استاندارد command2 متصل می‌کند.

این فرآیند در حافظه‌ی سیستم (RAM) اتفاق می‌افتد و هیچ فایل موقتی روی دیسک ایجاد نمی‌شود. این کار نه تنها تمیزتر و ساده‌تر است، بلکه بسیار بهینه‌تر و سریع‌تر نیز عمل می‌کند.

برای درک بهتر، فرض کنید می‌خواهیم لیست فایل‌های دایرکتوری /etc را ببینیم، اما چون لیست طولانی است، می‌خواهیم آن را صفحه به صفحه مشاهده کنیم. بدون پایپ باید این کار را می‌کردیم:

$ ls /etc > tempfile.txt
$ less tempfile.txt
$ rm tempfile.txt

اما با استفاده از پایپ، کل این سه مرحله به یک دستور واحد، خوانا و بهینه تبدیل می‌شود:

$ ls /etc | less

فیلترها (Filters)

بسیاری از ابزارهای خط فرمان لینوکس به عنوان «فیلتر» طراحی شده‌اند. فیلتر برنامه‌ای است که ورودی خود را از stdin می‌خواند، نوعی پردازش (مانند مرتب‌سازی، شمارش، جستجو و...) روی آن انجام می‌دهد و نتیجه را در stdout می‌نویسد. این طراحی ماژولار باعث می‌شود که این ابزارها کاملاً برای استفاده در زنجیره‌های پایپ مناسب باشند.

برخی از معروف‌ترین فیلترها که در ادامه دوره با بسیاری از آن‌ها آشنا خواهیم شد عبارتند از: sort (مرتب‌سازی)، uniq (حذف تکراری‌ها)، grep (جستجو)، wc (شمارش کلمات و خطوط)، head و tail (نمایش ابتدا و انتهای ورودی)، tr (جایگزینی کاراکترها) و بسیاری دیگر. قدرت واقعی خط فرمان زمانی آشکار می‌شود که شما یاد بگیرید چگونه این فیلترها را مانند قطعات لگو با استفاده از پایپ به هم متصل کنید.

مثال‌های کاربردی از Piping

بهترین راه برای درک قدرت پایپ، دیدن چند مثال عملی است.

مثال ۱: ترکیب ls و wc

فرض کنید می‌خواهیم تعداد فایل‌ها و دایرکتوری‌های موجود در پوشه‌ی /usr/bin را بشماریم. می‌توانیم خروجی دستور ls را به ورودی دستور wc (مخفف word count) بدهیم و با گزینه‌ی -l به آن بگوییم که فقط تعداد خطوط را بشمارد.

$ ls /usr/bin | wc -l
2145

این دستور به ما می‌گوید که خروجی دستور ls /usr/bin دارای ۲۱۴۵ خط است که معادل تعداد آیتم‌های موجود در آن دایرکتوری است.

مثال ۲: زنجیره‌ی چندتایی برای پردازش متن

ما می‌توانیم هر تعداد دستوری را که بخواهیم با پایپ به هم متصل کنیم. فرض کنید فایلی به نام fruits.txt با محتوای زیر داریم:

apple
banana
orange
apple
banana
apple

حالا می‌خواهیم یک لیست مرتب شده از میوه‌ها به همراه تعداد تکرار هر کدام را به دست آوریم. برای این کار یک زنجیره‌ی سه‌تایی می‌سازیم:

$ cat fruits.txt | sort | uniq -c
      3 apple
      2 banana
      1 orange

در اینجا چه اتفاقی افتاد؟

  1. cat fruits.txt محتوای فایل را می‌خواند و به خروجی استاندارد خود می‌فرستد.
  2. خروجی cat به ورودی sort پایپ می‌شود. sort خطوط را مرتب کرده و نتیجه را به خروجی خود می‌فرستد.
  3. خروجی مرتب‌شده‌ی sort به ورودی uniq -c پایپ می‌شود. دستور uniq خطوط تکراری پشت سر هم را حذف می‌کند و گزینه‌ی -c باعث می‌شود تعداد تکرار هر خط را نیز شمارش کند.

فلسفه‌ی یونیکس

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

  • برنامه‌هایی بنویسید که یک کار را به خوبی انجام دهند.
  • برنامه‌هایی بنویسید که با یکدیگر کار کنند.
  • برنامه‌هایی بنویسید که جریان‌های متنی را مدیریت کنند، زیرا این یک رابط جهانی است.

عملگر پایپ تجلی کامل این فلسفه در عمل است.