مقدمه

تاکنون ابزارهایی مانند grep را برای پیدا کردن خطوط حاوی یک الگو و wc را برای شمارش یاد گرفته‌ایم. اما اگر بخواهیم پردازش‌های پیچیده‌تری انجام دهیم، به خصوص روی داده‌هایی که ساختار ستونی دارند چطور؟ برای مثال، چگونه می‌توانیم فقط ستون پنجم از خروجی یک دستور را استخراج کنیم یا مجموع اعداد یک ستون خاص را محاسبه نماییم؟

اینجاست که awk وارد میدان می‌شود. awk (که نام آن از حروف اول نام خانوادگی سازندگانش، Aho، Weinberger و Kernighan گرفته شده) یک زبان برنامه‌نویسی کامل است که برای پردازش متن، به ویژه داده‌های ستونی، طراحی شده است. awk فایل‌ها را خط به خط می‌خواند، هر خط را به فیلدها (ستون‌ها) تقسیم می‌کند و به ما اجازه می‌دهد تا عملیات‌های مختلفی را روی این فیلدها انجام دهیم. تسلط بر awk یکی از مهارت‌هایی است که یک کاربر معمولی را از یک کاربر حرفه‌ای لینوکس متمایز می‌کند.

ساختار اصلی یک دستور awk

در نگاه اول، سینتکس awk ممکن است کمی پیچیده به نظر برسد، اما ساختار اصلی آن بسیار منطقی است:

$ awk 'pattern { action }' filename
  • action (عمل): این بخش که درون آکولاد {} قرار می‌گیرد، به awk می‌گوید که چه کاری را انجام دهد. مثلاً { print $1 } به معنای «ستون اول را چاپ کن» است.
  • pattern (الگو): این بخش یک شرط اختیاری است. اگر این شرط برای خط فعلی برقرار باشد، action مربوط به آن اجرا می‌شود. اگر هیچ الگویی مشخص نشود، action روی تمام خطوط ورودی اجرا می‌گردد.

مهم‌ترین مفهوم در awk، فیلدها هستند. awk به طور خودکار هر خط ورودی را بر اساس یک جداکننده (به طور پیش‌فرض، فاصله یا tab) به فیلدهایی تقسیم می‌کند و آن‌ها را با متغیرهای $1، $2، $3 و الی آخر در دسترس قرار می‌دهد. متغیر $0 نیز همیشه به کل خط فعلی اشاره دارد.

مثال‌های کاربردی با awk

بیایید با چند مثال عملی ببینیم awk چگونه کار می‌کند.

مثال ۱: چاپ ستون‌های خاص

خروجی دستور ls -l یک نمونه عالی از داده‌های ستونی است که با فاصله از هم جدا شده‌اند. اگر بخواهیم فقط ستون نهم (نام فایل) و ستون اول (مجوزها) را چاپ کنیم، می‌توانیم خروجی ls -l را به awk پایپ کنیم:

$ ls -l | awk '{print $9, $1}'
myscript.sh -rwxr-xr-x
another_file.txt -rw-r--r--
...

مثال ۲: استفاده از جداکننده فیلد متفاوت

همانطور که گفتیم، جداکننده پیش‌فرض، فاصله است. اما بسیاری از فایل‌ها مانند /etc/passwd از جداکننده‌ی دیگری (در این مورد، کولون :) استفاده می‌کنند. برای مشخص کردن جداکننده دلخواه، از گزینه‌ی -F استفاده می‌کنیم. دستور زیر، نام‌های کاربری (ستون اول) را از فایل /etc/passwd استخراج می‌کند:

$ awk -F':' '{print $1}' /etc/passwd
root
daemon
bin
...

مثال ۳: فیلتر کردن با استفاده از الگو

قدرت awk زمانی بیشتر می‌شود که از الگوها برای فیلتر کردن خطوط استفاده کنیم. الگو می‌تواند یک عبارت باقاعده (که در درس بعد با آن آشنا می‌شویم) یا یک شرط منطقی باشد. دستور زیر، فقط خطوطی از فایل /etc/passwd را پردازش می‌کند که حاوی کلمه bash باشند و سپس نام کاربری (ستون اول) و شل پیش‌فرض (ستون هفتم) آن کاربران را چاپ می‌کند.

$ awk -F':' '/bash/ {print "User:", $1, "uses shell:", $7}' /etc/passwd
User: root uses shell: /bin/bash
User: me uses shell: /bin/bash

بلوک‌های BEGIN و END

awk دو الگوی بسیار خاص به نام‌های BEGIN و END دارد که به ما کنترل بیشتری روی فرآیند پردازش می‌دهند.

  • بلوک BEGIN: دستورات داخل این بلوک، قبل از شروع خواندن اولین خط ورودی اجرا می‌شوند. این بلوک برای چاپ کردن عنوان (Header) یا مقداردهی اولیه متغیرها عالی است.
  • بلوک END: دستورات داخل این بلوک، بعد از پایان پردازش تمام خطوط ورودی اجرا می‌شوند. این بلوک برای انجام محاسبات نهایی و چاپ خلاصه‌ی نتایج ایده‌آل است.

مثال زیر نشان می‌دهد چگونه می‌توانیم مجموع اعداد یک ستون را محاسبه کنیم. فرض کنید فایلی به نام sales.txt با محتوای زیر داریم:

ProductA 15000
ProductB 22000
ProductC 18000

دستور awk زیر مجموع فروش (ستون دوم) را محاسبه و چاپ می‌کند:

$ awk 'BEGIN { print "Sales Report" } { sum += $2 } END { print "Total Sales:", sum }' sales.txt
Sales Report
Total Sales: 55000

در این دستور، بلوک BEGIN یک عنوان چاپ می‌کند، بلوک میانی به ازای هر خط، مقدار ستون دوم را به متغیر sum اضافه می‌کند و بلوک END در نهایت مقدار کل sum را نمایش می‌دهد.