通過兩個簡單的教程來提高你的 awk 技能
超越單行的 awk 腳本,學習如何做郵件合并和字數統計。
awk 是 Unix 和 Linux 用戶工具箱中最古老的工具之一。awk 由 Alfred Aho、Peter Weinberger 和 Brian Kernighan(即工具名稱中的 A、W 和 K)在 20 世紀 70 年代創建,用于復雜的文本流處理。它是流編輯器 sed 的配套工具,后者是為逐行處理文本文件而設計的。awk 支持更復雜的結構化程序,是一門完整的編程語言。
本文將介紹如何使用 awk 完成更多結構化的復雜任務,包括一個簡單的郵件合并程序。
awk 的程序結構
awk 腳本是由 {}(大括號)包圍的功能塊組成,其中有兩個特殊的功能塊,BEGIN 和 END,它們在處理第一行輸入流之前和最后一行處理之后執行。在這兩者之間,塊的格式為:
模式 { 動作語句 }
當輸入緩沖區中的行與模式匹配時,每個塊都會執行。如果沒有包含模式,則函數塊在輸入流的每一行都會執行。
另外,以下語法可以用于在 awk 中定義可以從任何塊中調用的函數。
function 函數名(參數列表) { 語句 }
這種模式匹配塊和函數的組合允許開發者結構化的 awk 程序,以便重用和提高可讀性。
awk 如何處理文本流
awk 每次從輸入文件或流中一行一行地讀取文本,并使用字段分隔符將其解析成若干字段。在 awk 的術語中,當前的緩沖區是一個記錄。有一些特殊的變量會影響 awk 讀取和處理文件的方式:
FS(字段分隔符)。默認情況下,這是任何空格字符(空格或制表符)。RS(記錄分隔符)。默認情況下是一個新行(n)。NF(字段數)。當awk解析一行時,這個變量被設置為被解析出字段數。$0:當前記錄。$1、$2、$3等:當前記錄的第一、第二、第三等字段。NR(記錄數)。迄今已被awk腳本解析的記錄數。
影響 awk 行為的變量還有很多,但知道這些已經足夠開始了。
單行 awk 腳本
對于一個如此強大的工具來說,有趣的是,awk 的大部分用法都是基本的單行腳本。也許最常見的 awk 程序是打印 CSV 文件、日志文件等輸入行中的選定字段。例如,下面的單行腳本從 /etc/passwd 中打印出一個用戶名列表:
awk -F":" '{print $1 }' /etc/passwd
如上所述,$1 是當前記錄中的第一個字段。-F 選項將 FS 變量設置為字符 :。
字段分隔符也可以在 BEGIN 函數塊中設置:
awk 'BEGIN { FS=":" } {print $1 }' /etc/passwd
在下面的例子中,每一個 shell 不是 /sbin/nologin 的用戶都可以通過在該塊前面加上匹配模式來打印出來:
awk 'BEGIN { FS=":" } ! /\/sbin\/nologin/ {print $1 }' /etc/passwd
awk 進階:郵件合并
現在你已經掌握了一些基礎知識,嘗試用一個更具有結構化的例子來深入了解 awk:創建郵件合并。
郵件合并使用兩個文件,其中一個文件(在本例中稱為 email_template.txt)包含了你要發送的電子郵件的模板:
From: Program committee <pc@event.org>To: {firstname} {lastname} <{email}>Subject: Your presentation proposalDear {firstname},Thank you for your presentation proposal:{title}We are pleased to inform you that your proposal has been successful! Wewill contact you shortly with further information about the eventschedule.Thank you,The Program Committee
而另一個則是一個 CSV 文件(名為 proposals.csv),里面有你要發送郵件的人:
firstname,lastname,email,titleHarry,Potter,hpotter@hogwarts.edu,"Defeating your nemesis in 3 easy steps"Jack,Reacher,reacher@covert.mil,"Hand-to-hand combat for beginners"Mickey,Mouse,mmouse@disney.com,"Surviving public speaking with a squeaky voice"Santa,Claus,sclaus@northpole.org,"Efficient list-making"
你要讀取 CSV 文件,替換第一個文件中的相關字段(跳過第一行),然后把結果寫到一個叫 acceptanceN.txt 的文件中,每解析一行就遞增文件名中的 N。
把 awk 程序寫在一個叫 mail_merge.awk 的文件中。在 awk 腳本中的語句用 ; 分隔。第一個任務是設置字段分隔符變量和其他幾個腳本需要的變量。你還需要讀取并丟棄 CSV 中的第一行,否則會創建一個以 Dear firstname 開頭的文件。要做到這一點,請使用特殊函數 getline,并在讀取后將記錄計數器重置為 0。
BEGIN {FS=",";template="email_template.txt";output="acceptance";getline;NR=0;}
主要功能非常簡單:每處理一行,就為各種字段設置一個變量 —— firstname、lastname、email 和 title。模板文件被逐行讀取,并使用函數 sub 將任何出現的特殊字符序列替換為相關變量的值。然后將該行以及所做的任何替換輸出到輸出文件中。
由于每行都要處理模板文件和不同的輸出文件,所以在處理下一條記錄之前,需要清理和關閉這些文件的文件句柄。
{# 從輸入文件中讀取關聯字段firstname=$1;lastname=$2;email=$3;title=$4;# 設置輸出文件名outfile=(output NR ".txt");# 從模板中讀取一行,替換特定字段,# 并打印結果到輸出文件。while ( (getline ln < template) > 0 ){sub(/{firstname}/,firstname,ln);sub(/{lastname}/,lastname,ln);sub(/{email}/,email,ln);sub(/{title}/,title,ln);print(ln) > outfile;}# 關閉模板和輸出文件,繼續下一條記錄close(outfile);close(template);}
你已經完成了! 在命令行上運行該腳本:
awk -f mail_merge.awk proposals.csv
或
awk -f mail_merge.awk < proposals.csv
你會在當前目錄下發現生成的文本文件。
awk 進階:字頻計數
awk 中最強大的功能之一是關聯數組,在大多數編程語言中,數組條目通常由數字索引,但在 awk 中,數組由一個鍵字符串進行引用。你可以從上一節的文件 proposals.txt 中存儲一個條目。例如,在一個單一的關聯數組中,像這樣:
proposer["firstname"]=$1;proposer["lastname"]=$2;proposer["email"]=$3;proposer["title"]=$4;
這使得文本處理變得非常容易。一個使用了這個概念的簡單的程序就是詞頻計數器。你可以解析一個文件,在每一行中分解出單詞(忽略標點符號),對行中的每個單詞進行遞增計數器,然后輸出文本中出現的前 20 個單詞。
首先,在一個名為 wordcount.awk 的文件中,將字段分隔符設置為包含空格和標點符號的正則表達式:
BEGIN {# ignore 1 or more consecutive occurrences of the characters# in the character group belowFS="[ .,:;()<>{}@!\"'\t]+";}
接下來,主循環函數將遍歷每個字段,忽略任何空字段(如果行末有標點符號,則會出現這種情況),并遞增行中單詞數:
{for (i = 1; i <= NF; i++) {if ($i != "") {words[$i]++;}}}
最后,處理完文本后,使用 END 函數打印數組的內容,然后利用 awk 的能力,將輸出的內容用管道輸入 shell 命令,進行數字排序,并打印出 20 個最常出現的單詞。
END {sort_head = "sort -k2 -nr | head -n 20";for (word in words) {printf "%s\t%d\n", word, words[word] | sort_head;}close (sort_head);}
在這篇文章的早期草稿上運行這個腳本,會產生這樣的輸出:
[dneary@dhcp-49-32.bos.redhat.com]$ awk -f wordcount.awk < awk_article.txtthe 79awk 41a 39and 33of 32in 27to 26is 25line 23for 23will 22file 21we 16We 15with 12which 12by 12this 11output 11function 11
下一步是什么?
如果你想了解更多關于 awk 編程的知識,我強烈推薦 Dale Dougherty 和 Arnold Robbins 所著的《Sed 和 awk》這本書。
awk 編程進階的關鍵之一是掌握“擴展正則表達式”。awk 為你可能已經熟悉的 sed 正則表達式語法提供了幾個強大的補充。
另一個學習 awk 的好資源是 GNU awk 用戶指南。它有一個完整的 awk 內置函數庫的參考資料,以及很多簡單和復雜的 awk 腳本的例子。

























