Awk، یک زبان script نویسی همه منظوره است که با هدف پردازش متن پیشرفته طراحی شده است. این زبان، بیشتر به عنوان یک ابزار گزارش و تحلیل استفاده می‌شود.

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

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

 

 

نحوه عملکرد

چندین پیاده‌سازی مختلف از awk وجود دارد. در اینجا، از پیاده‌سازی مربوط به GNU استفاده می‌کنیم که به آن gawk می‌گویند. در اکثر سیستم‌های لینوکس، مفسر awk تنها یک لینک به gawk است.

 

رکوردها و فیلدها

Awk می‌تواند فایل‌های داده متنی و streamها را پردازش کند. داده‌های ورودی به رکوردها و فیلدها تقسیم می‌شوند. Awk در هر بار روی یک رکورد عمل می‌کند تا زمانی که ورودی به پایان برسد. رکوردها توسط کاراکتری به نام جداکننده رکورد (record seprator) از هم جدا می‌شوند. جداکننده رکورد پیش فرض، کاراکتر خط جدید است؛ به این معنی که هر خط در داده‌های متنی یک رکورد است. یک جداکننده رکورد جدید را می‌توان با استفاده از متغیر RS تنظیم کرد.

رکوردها شامل فیلدهایی هستند که با جداکننده فیلد از هم جدا می‌شوند. به طور پیش فرض، فیلدها با یک فضای خالی شامل یک یا چند tab، فاصله و کاراکترهای خط جدید از هم جدا می‌شوند.

فیلدهای هر رکورد با علامت دلار ($) و به دنبال آن شماره فیلد، که با 1 شروع می‌شود، ارجاع داده می‌شوند. فیلد اول با $1، فیلد دوم با $2، و ... نشان داده می‌شود. آخرین فیلد را می‌توان با متغیر ویژه $NF نیز ارجاع داد. کل رکورد را می‌توان با $0 ارجاع داد.

در اینجا یک نمایش بصری در مورد نحوه ارجاع به رکوردها و فیلدها نمایش داده شده است:

tmpfs      788M  1.8M  786M   1% /run/lock 
/dev/sda1  234G  191G   31G  87% /
|-------|  |--|  |--|   |--| |-| |--------| 
   $1       $2    $3     $4   $5  $6 ($NF) --> fields
|-----------------------------------------| 
                    $0                     --> record

 

برنامه awk

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

pattern { action }
pattern { action }
...

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

اقدامات تعریف شده در awk در کاراکتر {} محصور شده و شامل چندین عبارت است که هر عبارت عملیاتی را که باید انجام شود، مشخص می‌کند. یک action می‌تواند بیش از یک عبارت داشته باشد که با خط جدید یا نقطه ویرگول (;) از هم جدا شده‌اند. اگر قانون هیچ actionای نداشته باشد، به طور پیش‌فرض کل رکورد را چاپ می‌کند.

 

Awk انواع مختلفی از عبارات را پشتیبانی می‌کند، از جمله expressionها، شرط‌ها، دستورات ورودی، خروجی و غیره. رایج‌ترین عبارات awk به شرح زیر هستند:

exit: اجرای کل برنامه را متوقف کرده و خارج می‌شود.

next: پردازش رکورد فعلی را متوقف کرده و به رکورد بعدی در داده‌های ورودی می‌رود.

print: رکوردها، فیلدها، متغیرها و متن سفارشی را چاپ می‌کند.

printf: به شما کنترل بیشتری بر فرمت خروجی می‌دهد، مشابه C و bash printf.

هنگام نوشتن برنامه‌های awk، تا انتهای خط هر چیزی که بعد از علامت هش (#) باشد، یک comment در نظر گرفته می‌شود. خطوط طولانی را می‌توان با استفاده از کاراکتر ادامه یعنی بک اسلش (\) به چند خط تقسیم کرد.

 

اجرای برنامه‌های awk

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

awk 'program' input-file...

هنگامی‌که برنامه را در خط فرمان اجرا می‌کنید، باید آن را در کاراکتر ' ' محصور کرد، بنابراین shell برنامه را تفسیر نمی‌کند.

اگر برنامه بزرگ و پیچیده است، بهتر است آن را در یک فایل قرار داده و از گزینه f- برای ارسال فایل به دستور awk استفاده نمایید:

awk -f program-file input-file...

در مثال‌های زیر، از فایلی با نام “teams.txt” استفاده خواهیم کرد که شامل خطوط زیر است:

Bucks Milwaukee    60 22 0.732 
Raptors Toronto    58 24 0.707 
76ers Philadelphia 51 31 0.622
Celtics Boston     49 33 0.598
Pacers Indiana     48 34 0.585

 

این مطلب نیز ممکن است برای شما مفید باشد: جستجو در محتویات فایل با استفاده از دستور grep

 

الگوهای Awk

الگوهای موجود در awk کنترل می‌کنند که آیا action مرتبط باید اجرا شود یا خیر.

Awk از انواع مختلف الگوها، از جمله عبارات منظم، عبارات رابطه‌ای، عبارات محدوده‌ای و عبارات خاص پشتیبانی می‌کند.

هنگامی‌که قانون بدون الگو باشد، هر رکورد ورودی مطابقت خواهد داشت. در اینجا یک مثال از یک قانون است که شامل تنها یک action است:

awk '{ print $3 }' teams.txt

برنامه، فیلد سوم هر رکورد را به صورت زیر چاپ می‌کند:

output:
60
58
51
49
48

 

الگوهای عبارات منظم

یک عبارت منظم یا regex الگویی است که با مجموعه‌ای از رشته‌ها مطابقت دارد. الگوهای عبارت منظم Awk درون دو اسلش (//) قرار می‌گیرند:

/regex pattern/ { action }

ابتدایی‌ترین مثال، یک کاراکتر تحت اللفظی یا تطبیق رشته است. به عنوان مثال، برای نمایش اولین فیلد هر رکورد که حاوی "0.5" است، باید دستور زیر را اجرا نمایید:

awk '/0.5/ { print $1 }' teams.txt
output:
Celtics
Pacers

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

awk '/^[0-9][0-9]/ { print $1 }' teams.txt
output:
76ers

 

الگوهای عبارات رابطه‌ای

الگوهای عبارات رابطه‌ای معمولاً برای مطابقت با محتوای یک فیلد یا متغیر خاص استفاده می‌شوند.

به طور پیش فرض، الگوهای عبارات منظم با رکوردها مطابقت دارند. برای تطبیق یک regex با یک فیلد، فیلد را مشخص کرده و از "contain" یا عملگر مقایسه (~) برای الگو استفاده کنید.

به عنوان مثال، به منظور چاپ اولین فیلد هر رکورد که فیلد دوم آن حاوی "ia" است، باید به صورت زیر عمل نمایید:

awk '$2 ~ /ia/ { print $1 }' teams.txt
Output:
76ers
Pacers

 

به منظور تطبیق فیلدهایی که دارای الگوی مشخصی نیستند از عملگر !~ استفاده نمایید:

awk '$2 !~ /ia/ { print $1 }' teams.txt
output:
Bucks
Raptors
Celtics

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

awk '$3 > 50 { print $1 }' teams.txt
Bucks
Raptors
76ers

 

الگوهای محدوده (Range)

الگوهای محدوده شامل دو الگو هستند که با کاما از هم جدا می‌شوند:

pattern1, pattern2

یک دسته از رکوردها که در آن اولین رکورد با الگوی اول منطبق می‌شود و آخرین رکورد با الگوی دوم منطبق می‌شود، مطابقت خواهند داشت.

در اینجا یک مثال آورده شده است که اولین فیلد همه رکوردها از رکورد شامل "Raptors" تا رکورد شامل "Celtics" را چاپ می‌کند:

awk '/Raptors/,/Celtics/ { print $1 }' teams.txt
output:
Raptors
76ers
Celtics

الگوها همچنین می‌توانند عبارات رابطه‌ای باشند. دستور زیر تمام رکوردها را از رکوردی که فیلد چهارم آن برابر با 32 است تا رکوردی که فیلد چهارم آن برابر با 33 است چاپ می‌کند:

awk '$4 == 31, $4 == 33 { print $0 }' teams.txt
output:
76ers Philadelphia 51 31 0.622
Celtics Boston     49 33 0.598

دقت نمایید که الگوهای محدوده را نمی‌توان با سایر عبارات الگو ترکیب کرد.

 

الگوهای عبارات خاص

Awk شامل الگوهای ویژه زیر است:

BEGIN: برای انجام اقدامات قبل از پردازش رکوردها استفاده می‌شود.

END: برای انجام اقدامات پس از پردازش رکوردها استفاده می‌شود.

الگوی BEGIN به طور کلی برای تنظیم متغیرها و الگوی END برای پردازش داده‌ها از رکوردها مانند محاسبات استفاده می‌شود.

مثال زیر "Start Processing" را چاپ می‌کند، سپس قسمت سوم هر رکورد و در نهایت "End Processing" را چاپ می‌کند:

awk 'BEGIN { print "Start Processing." }; { print $3 }; END { print "End Processing." }' teams.txt
ouput:
Start Processing
60
58
51
49
48
End Processing.

اگر یک برنامه، تنها یک الگوی BEGIN داشته باشد، actionها اجرا می‌شوند ولی ورودی پردازش نمی‌شود. اگر یک برنامه، تنها یک الگوی END داشته باشد، ورودی قبل از اجرای actionهای قانون پردازش می‌شود.

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

 

ترکیب الگوها

Awk به شما امکان می‌دهد دو یا چند الگو را با استفاده از عملگر AND منطقی (&&) و عملگر OR منطقی (||) ترکیب نمایید.

در ادامه، نمونه‌ای ارائه شده است که از عملگر && برای چاپ اولین فیلد از رکوردهایی که فیلد سوم آن‌ها بزرگ‌تر از 50 و فیلد چهارم آن‌ها کوچک‌تر از 30 است، استفاده می‌کند:

awk '$3 > 50 && $4 < 30 { print $1 }' teams.txt
Bucks
Raptors

 

این مطلب نیز ممکن است برای شما مفید باشد: آشنایی با مجوزهای فایل لینوکس

 

متغیرهای داخلی Awk

Awk چندین متغیر داخلی دارد که حاوی اطلاعات مفیدی هستند و به شما امکان می‌دهند نحوه پردازش برنامه را کنترل کنید. در ادامه به شرح برخی از متداول‌ترین متغیرهای داخلی Awk پرداخته شده است:

NF: تعداد فیلدهای رکورد را نمایش می‌دهد.

NR: تعداد رکوردهای فعلی را نشان می‌دهد.

FILENAME: نام فایل ورودی که در حال حاضر پردازش می‌شود را نمایش می‌دهد.

FS: جداکننده فیلدها را نشان می‌دهد.

RS: جداکننده رکورد را نشان می‌دهد.

OFS: جداکننده فیلد خروجی را نشان می‌دهد.

ORS: جداکننده رکورد خروجی را نشان می‌دهد.

در ادامه، مثالی وجود دارد که نحوه چاپ نام فایل و تعداد خطوط (رکوردها) را نشان می‌دهد:

awk 'END { print "File", FILENAME, "contains", NR, "lines." }' teams.txt
output:
File teams.txt contains 5 lines.

متغیرها در AWK می‌توانند در هر خطی از برنامه تنظیم شوند. به منظور تعریف یک متغیر برای کل برنامه، آن را در یک الگوی BEGIN قرار دهید.

 

تغییر جداکننده فیلد و رکورد

مقدار پیش‌فرض جداکننده فیلد، تعدادی کاراکتر فاصله یا tab است. این مقدار را می‌توان با تنظیم متغیر FS تغییر داد.

به عنوان مثال، برای تنظیم جداکننده فیلد بر روی کاراکتر dot می‌توانید از دستور زیر استفاده نمایید:

awk 'BEGIN { FS = "." } { print $1 }' teams.txt
output:
Bucks Milwaukee    60 22 0
Raptors Toronto    58 24 0
76ers Philadelphia 51 31 0
Celtics Boston     49 33 0
Pacers Indiana     48 34 0

جداکننده فیلد همچنین می‌تواند روی بیش از یک کاراکتر تنظیم شود:

awk 'BEGIN { FS = ".." } { print $1 }' teams.txt

هنگام اجرای awk one-liners در خط فرمان، شما می‌توانید از گزینه F- نیز برای تغییر جداکننده فیلد استفاده کنید:

awk -F "." '{ print $1 }' teams.txt

 

به طور پیش فرض، جداکننده رکورد، یک کاراکتر خط جدید است و با استفاده از متغیر RS می‌توان مقدار آن را تغییر داد.

در مثال زیر، جداکننده رکورد برروی مقدار dot تنظیم می‌شود:

awk 'BEGIN { RS = "." } { print $1 }' teams.txt
output:
Bucks Milwaukee    60 22 0
732 
Raptors Toronto    58 24 0
707 
76ers Philadelphia 51 31 0
622
Celtics Boston     49 33 0
598
Pacers Indiana     48 34 0
585

 

actionهای Awk

همانطور که قبلا بیان شد، actionهای Awk داخل کاراکترهای {} قرار می‌گیرند و زمانی که تطابقی با الگو یافت شود، اجرا می‌شوند. یک action می‌تواند صفر یا چند عبارت داشته باشد. ترتیب اجرای عبارات چندگانه به ترتیب ظاهر شدن آن‌ها است. این عبارات با خط جدید یا نقطه ویرگول (;) از هم جدا می‌شوند.

انواع مختلفی از عبارات action وجود دارد که در awk پشتیبانی می‌شوند:

Expression؛ مانند انتصاب متغیر، عملگرهای حسابی، عملگرهای افزایشی و کاهشی.

Control statement؛ مورد استفاده برای کنترل جریان برنامه؛ مانند if ،for ،while ،switch و ... .

Output statment؛ مانند print و printf.

Compound statement؛ مورد استفاده برای گروه‌بندی عبارات دیگر.

Input statement؛ مورد استفاده برای کنترل پردازش ورودی.

Deletion statement؛ مورد استفاده برای حذف عناصر آرایه.

 

دستور print معمولاً پر استفاده‌ترین دستور awk است که یک خروجی فرمت‌دار از متن، رکوردها، فیلدها و متغیرها را چاپ می‌کند.

هنگام چاپ چندین مورد، باید آن‌ها را با کاما از هم جدا نمایید. به عنوان مثال:

awk '{ print $1, $3, $5 }' teams.txt

موارد چاپ شده با فاصله از هم جدا می‌شوند:

output:
Bucks 60 0.732
Raptors 58 0.707
76ers 51 0.622
Celtics 49 0.598
Pacers 48 0.585

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

awk '{ print $1 $3 $5 }' teams.txt

همانطور که مشاهده می‌کنید، در خروجی مقادیر چاپ شده به هم پیوسته‌اند:

output:
Bucks600.732
Raptors580.707
76ers510.622
Celtics490.598
Pacers480.585

 

هنگامی‌که print بدون آرگومان استفاده می‌شود، به طور پیش فرض print $0 در نظر گرفته می‌شود و رکورد فعلی چاپ می‌شود.

برای چاپ یک متن سفارشی، باید متن را داخل کاراکترهای “” قرار دهید.

awk '{ print "The first field:", $1}' teams.txt
output:
The first field: Bucks
The first field: Raptors
The first field: 76ers
The first field: Celtics
The first field: Pacers

بدین صورت، شما همچنین می‌توانید کاراکترهای خاص مانند خط جدید را چاپ نمایید:

awk 'BEGIN { print "First line\nSecond line\nThird line" }'
output:
First line
Second line
Third line

دستور printf به شما کنترل بیشتری برروی فرمت خروجی می‌دهد. در مثال زیر، شماره خطوط به همراه خروجی درج شده است:

awk '{ printf "%3d. %s\n", NR, $0 }' teams.txt

printf بعد از هر رکورد یک خط جدید ایجاد نمی‌کند، بنابراین ما از n\ استفاده می‌کنیم:

output:
  1. Bucks Milwaukee    60 22 0.732 
  2. Raptors Toronto    58 24 0.707 
  3. 76ers Philadelphia 51 31 0.622
  4. Celtics Boston     49 33 0.598
  5. Pacers Indiana     48 34 0.585

 

دستور زیر، مجموع مقادیر ذخیره شده در فیلد سوم را در هر خط محاسبه می‌کند:

awk '{ sum += $3 } END { printf "%d\n", sum }' teams.txt
output:
266

در ادامه، مثال دیگری وجود دارد که نحوه استفاده از عبارات و دستورات کنترل را برای چاپ مربع اعداد از 1 تا 5 نشان می‌دهد:

awk 'BEGIN { i = 1; while (i < 6) { print "Square of", i, "is", i*i; ++i } }'
Square of 1 is 1
Square of 2 is 4
Square of 3 is 9
Square of 4 is 16
Square of 5 is 25

درک و کار با دستورات تک خطی مانند دستور بالا سخت‌تر است؛ بنابراین هنگام نوشتن برنامه‌های طولانی‌تر، باید یک فایل برنامه جداگانه ایجاد نمایید:

                     prg.awk

BEGIN { 
  i = 1
  while (i < 6) { 
    print "Square of", i, "is", i*i; 
    ++i 
  } 
}

برنامه را با ارسال نام فایل به مفسر awk اجرا کنید:

awk -f prg.awk

 

شما همچنین می‌توانید با استفاده از دستور shebang و تنظیم مفسر awk، یک برنامه awk را به عنوان یک فایل اجرایی اجرا نمایید:

                            prg.awk

#!/usr/bin/awk -f
BEGIN { 
  i = 1
  while (i < 6) { 
    print "Square of", i, "is", i*i; 
    ++i 
  } 
}

فایل را ذخیره کرده و سپس آن را قابل اجرا نمایید:

chmod +x prg.awk

اکنون شما می‌توانید برنامه را با وارد کردن دستور زیر اجرا کنید:

./prg.awk

 

این مطلب نیز ممکن است برای شما مفید باشد: مقایسه دو فایل در لینوکس با استفاده از diff ،vimdiff و colordiff

 

استفاده از متغیرهای shell در برنامه‌های Awk

اگر از دستور awk در اسکریپت‌های shell استفاده می‌کنید، به احتمال زیاد باید یک متغیر shell را به برنامه awk ارسال نمایید. یکی از گزینه‌ها این است که برنامه را به جای ‘ ’ با “ ” محصور کرده و متغیر را در برنامه جایگزین نمایید. با این حال، این گزینه برنامه awk شما را پیچیده‌تر می‌کند؛ زیرا باید از متغیرهای awk بگذرید.

روش پیشنهادی برای استفاده از متغیرهای shell در برنامه‌های awk، اختصاص متغیر shell به متغیر awk است. به عنوان مثال:

num=51
awk -v n="$num" 'BEGIN {print n}'
51

 

 

 

منبع:

linuxize