Linux 伺服器系統管理第十二章 Sed 與 awk 處理工具  上一頁    

12-4 Awk 命令稿

內容:

當一行命令敘述無法完成任務時,則可考慮將 awk 的搜尋與動作編寫成一個命令稿,再以批次執行方式處理,也許較容易達成目的。Awk 命令稿執行方式如下:

$ awk –f script_file input_file

其中 script_file 為檢索 input_file 檔案的命令稿,大部分 awk 搜尋與動作敘述都可被編寫入。檢索處理後的結果將會顯示在螢幕上,也可將它轉向輸出到其他檔案上(如 > output_file)。

12-4-1 awk 命令稿格式

某一公司的員工資料檔案(employee)包含有員工姓名、電話、時薪與工作時數,我們可利用此檔案來簡略說明 awk 命令稿的編寫方式。

A. Awk 範例一】

編寫一個命令稿(list.awk,檔案名稱與型態可以任意命名),其功能是列印出 employee 檔案內的所有資料;處理資料之前先印出一行各欄位的名稱(BEGIN),再一筆接一筆連續印出內容;命令稿的內容及執行結果如下:

[tsnien@Linux-1 tools]$ cat list.awk

BEGIN {

    print "姓名", "\t", "電話", "\t\t", "時薪", "\t", "時數"

}

{ print $1, "\t", $2, "\t", $3, "\t", $4 }

[tsnien@Linux-1 tools]$ awk -f list.awk employee

姓名     電話            時薪    時數

Frank    07-234567       412.0   25

George   04-384123       217.0   18

Nacy     06-672314       516.0   45

Louis    07-384675       311.0   32

Eva      04-243890       358.5   38

Tank     06-631289       482.5   42

我們可以看出基本命令稿的語法幾乎與命令行語法相同,唯一不同的是不必用兩個單引號(’ …’)包起來。此程式的執行步驟如下:

(1) 步驟 1:還未讀取輸入檔案之前,首先執行 BEGIN 敘述,也就是印出姓名、電話、時薪與時數等字串。

(2) 步驟 2:讀取檔案內一行資料(即是一筆資料),並依照欄位位置將其內容填入 $1$2$3 $4 變數內。

(3) 步驟 3 awk 敘述中沒有搜尋條件,表示每一筆資料都必須依照後面指定動作執行,亦即印出各變數內容(print …)。

(4) 步驟 4:如果檔案內還有下一筆資料,則回到步驟 2 繼續執行,否則結束讀取檔案,並執行 END 敘述內容(本範例沒有 END)。

B. Awk 範例二】

搜尋並印出電話在高雄的員工資料,命令稿(find.awk)與執行結果如下:

[tsnien@Linux-1 tools]$ cat find.awk

/07-/ {print}

[tsnien@Linux-1 tools]$ awk -f find.awk employee

Frank   07-234567       412.0   25

Louis   07-384675       311.0   32

C. Awk 範例三】

印出工作時數超過 30 小時員工的姓名、時薪與工作時數,命令稿(time.awk)與執行結果如下:

[tsnien@Linux-1 tools]$ cat time.awk

$4 >= 30 {print $1, "\t", $3, "\t", $4}

[tsnien@Linux-1 tools]$ awk -f time.awk employee

Nacy     516.0   45

Louis    311.0   32

Eva      358.5   38

Tank     482.5   42

本範例執行動作如下:當 awk 讀入每一資料後,首先判斷 $4(第四欄位,即是工作時數)是否超過 30,如果是則印出欄位 134 的內容;接著再讀取下一筆,直到檔案內全部讀完為止。

D. Awk 範例四】

計算出本月每一員工的應領薪資,以及全部薪資總額,命令稿(total.awk)及操作範例如下:

[tsnien@Linux-1 tools]$ cat total.awk

BEGIN {print "Name", "\t", "Payment"}

{

 payment = $3 * $4

 print $1, "\t", payment

 total = total + payment

}

END {print "Total payment = ", total}

[tsnien@Linux-1 tools]$ awk -f total.awk employee

Name     Payment

Frank    10300

George   3906

Nacy     23220

Louis    9952

Eva      13623

Tank     20265

Total payment =  81266

簡略說明 total.awk 的運作方式,還未讀取輸入檔案(employee)之前,先印出一行’Name’’Payment’字樣(BEGIN 敘述)。接著按照輸入檔案內資料的排列次序,每次讀入一筆記錄,並計算個人薪資(payment = $3 * $4),以及印出員工姓名及個人薪資,再累積計算總共薪資(total = total + payment)。輸入檔案內資料讀取完畢,最後印出所有薪資的總合(END 敘述)。

E. Awk 範例五】

列出時薪超過 350 元的員工姓名與應領薪資,以及平均薪資多少,命令稿(hi-pay.awk)及執行結果如下:

[tsnien@Linux-1 tools]$ cat hi-pay.awk

BEGIN {print "Name", "\t", "Payment"}

$3 >= 350 {

   payment = $3 * $4

   total = total + payment

   number = number +1

   print $1, "\t", payment

}

END {

    average = total / number

    print "High payments = ", number

    print "Average payment = ", average

}

[tsnien@Linux-1 tools]$ awk -f hi-pay.awk employee

Name     Payment

Frank    10300

Nacy     23220

Eva      13623

Tank     20265

High payments =  4

Average payment =  16852

F. Awk 範例六】

印出時薪低於 400 元而且應領薪資低於 10000 元的員工姓名與應領薪資,並印出人數與其平均薪資多少。命令稿(lowpay.awk)及操作範例如下:

[tsnien@Linux-1 tools]$ cat lowpay.awk

BEGIN {

  printf("Name\t Payment\n");

}

($3 < 400) {

   payment = $3 * $4;

   if (payment < 10000) {

      total = total + payment;

      number = number +1;

      printf("%s\t %d\n", $1, payment);

   }

}

END {

   printf("Low payments = %d\n", number);

   printf("Average payment = %.2f\n", total / number);

}

[tsnien@Linux-1 tools]$ awk -f lowpay.awk employee

Name     Payment

George   3906

Louis    9952

Low payments = 2

Average payment = 6929.00

由上述 lowpay.awk 範例可以看出,awk 動作敘述(action)幾乎與 C 語言的語法相同;不僅如此,其他控制敘述、函數宣告的語法,也幾乎相同,這對我們學習 awk 語法有相當的幫助。值得注意的是,awk 搜尋敘述包含許多文件處理的特殊語法(如正規式圖樣搜尋),因此保有自己獨特的語法,但也大多與其他過濾器工具(如 grep sed)相似。

12-4-2 Awk 控制敘述

Awk 控制敘述的語法幾乎與 C 語言相同,這對我們在學習上顯得格外容易,這裡用幾個範例來說明各種控制敘述的使用方法。以下各個範例使用一個輸入檔案(course 檔案),檔案中每一筆資料(每一行)表示學生的姓名、英文、數學、程式設計、作業系統與資料結構的成績(由左到右依序排列),內容如下所示:

[tsnien@Linux-1 tools]$ cat course

Frank   60 70 90 80 73

George  50 40 60 70 72

Nacy    90 73 56 75 82

Louis   90 67 89 73 56

Eva     40 56 72 45 51

Tank    72 80 69 90 92

A. if 控制敘述】

if 條件敘述的語法如下:

if (expression)

    action_1

[else

    action_2]

如果 expression 條件判斷成立,則執行 action_1 敘述;否則執行 action_2 敘述,其中 else 敘述選項([…]),依照需求決定是否加入。我們利用一個範例(if.awk)來說明 if 敘述的使用方法,該程式可計算並輸出 course 檔案中每一個學生的平均分數,並標示是否及格(平均超過 70 分及格),命令稿(if.awk)與執行結果如下:

[tsnien@Linux-1 tools]$ cat if.awk

{

    sum = $2 + $3 +$4 + $5 +$6;

    average = sum / 5;

    if (average > 70)

        grade = "Pass";

    else

        grade = "Fail";

    printf("%s course = %d and  %s\n", $1, average, grade);

}

[tsnien@Linux-1 tools]$ awk -f if.awk course

Frank course = 74 and  Pass

George course = 58 and  Fail

Nacy course = 75 and  Pass

Louis course = 75 and  Pass

Eva course = 52 and  Fail

Tank course = 80 and  Pass

if.awk 命令稿內並沒有搜尋條件,表示每一筆資料都必須處理。Awk 依序讀取每一筆資料,並計算其總合(sum)及平均分數(average),接著再判斷是否有超過 70 分。執行當中,awk 會依序讀完所有資料,因此無需指定多少筆記錄。

B. while 迴圈敘述】

while 迴圈語法如下:

while (condition)

    action

條件 condition 成立則執行 action 部分。以下範例是計算並印出每一學生的學其平均分數,命令稿(while.awk)與操作結果如下:

[tsnien@Linux-1 tools]$ cat while.awk

{

    sum = 0;

    count = 2;

    while (count <= NF) {

        sum = sum + $count;

        count++

    }

    average = sum / (NF-1);

    printf("%s average  = %d\n", $1, average);

}

[tsnien@Linux-1 tools]$ awk -f while.awk course

Frank average  = 74

George average  = 58

Nacy average  = 75

Louis average  = 75

Eva average  = 52

Tank average  = 80

動作敘述(action)部分如果超過一個敘述以上,則必須利用左右大括號包起來,這方面與 C 語言相同。

C. do 迴圈敘述】

do 迴圈敘述的語法如下:

do

    action

while (condition)

如同 while 迴圈的範例,如下:

[tsnien@Linux-1 tools]$ cat do.awk

{

    sum = 0;

    count = 2;

    do {

        sum = sum + $count;

        count++

    }while(count <= NF);

    average = sum / (NF-1);

    printf("%s average  = %d\n", $1, average);

}

[tsnien@Linux-1 tools]$ awk -f do.awk course

Frank average  = 74

George average  = 58

Nacy average  = 75

Louis average  = 75

Eva average  = 52

Tank average  = 80

D. for 迴圈敘述】

 for 迴圈敘述的語法與 C 語言很類似,格式如下:

for (set_counter ; test_counter ; calculate_counter)

    action

其中 set_counter 為設定計數器的初始值;test_counter 為設定一個測試條件,條件成立則執行迴圈內 action 敘述,執行後再依照 calculate_counter 方式計算計數器的內容,大多是遞增或遞減方式。每次執行完 for 迴圈內 action 敘述後,會再重新測試 test_counter,如果不成立的話,則離開並結束 for 迴圈執行。另外,action 如果超過一個敘述的話,則必須利用左右大括號包起來。範例如下:

[tsnien@Linux-1 tools]$ cat for.awk

# awk for-loop example

{

    sum = 0;

    for (count=2; count <= NF; count++)

        sum = sum + $count;

    average = sum / (NF-1);

    printf("%s average  = %d\n", $1, average);

}

[tsnien@Linux-1 tools]$ awk -f for.awk course

Frank average  = 74

George average  = 58

Nacy average  = 75

Louis average  = 75

Eva average  = 52

Tank average  = 80

E. break continue 敘述】

我們可以利用 break continue 敘述來改變 forwhile do 迴圈的正常運作,它們的語法大多與 C 語言相似,這裡就不再贅言了。

F. 變數傳遞】

執行 awk 命令稿可區分為 BEGINaction END 三個程式區塊,其變數是可以相互引用的。以下範例是計算出 course 檔案內,該班及各科成績的平均值。首先在 BEGIN 區塊內設定變數,接著在 action 區塊內累計各科成績,最後再由 END 區塊內計算出各科成績的平均值(NR 為讀入記錄的筆數)。其命令稿(var.awk)與操作結果如下:

[tsnien@Linux-1 tools]$ cat var.awk

BEGIN {

    eng=0; math=0; prog=0; os=0; data=0;

}

{

    eng = eng + $2;

    math = math + $3;

    prog = prog + $4;

    os = os + $5;

    data = data +$6;

}

END  {

   printf("英文  數學   程式設計  作業系統  資料結構\n");

   printf("%d\t%d\t%d\t%d\t%d\n", eng/NR, math/NR, prog/NR,

os/NR, data/NR);

}

[tsnien@Linux-1 tools]$ awk -f var.awk course

英文  數學   程式設計  作業系統  資料結構

67      64      72      72      71

 

翻轉工作室:粘添壽

 

Linux 伺服器系統管理 - CentOS:

 

 

 

翻轉電子書系列: