通過兩個簡單的教程來提高你的 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 proposal
Dear {firstname},
Thank you for your presentation proposal:
{title}
We are pleased to inform you that your proposal has been successful! We
will contact you shortly with further information about the event
schedule.
Thank you,
The Program Committee
而另一個則是一個 CSV 文件(名為 proposals.csv
),裡面有你要發送郵件的人:
firstname,lastname,email,title
Harry,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 below
FS="[ .,:;()<>{}@!"'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 "%st%dn", word, words[word] | sort_head;
}
close (sort_head);
}
在這篇文章的早期草稿上運行這個腳本,會產生這樣的輸出:
[dneary@dhcp-49-32.bos.redhat.com]$ awk -f wordcount.awk < awk_article.txt
the 79
awk 41
a 39
and 33
of 32
in 27
to 26
is 25
line 23
for 23
will 22
file 21
we 16
We 15
with 12
which 12
by 12
this 11
output 11
function 11
下一步是什麼?
如果你想了解更多關於 awk
編程的知識,我強烈推薦 Dale Dougherty 和 Arnold Robbins 所著的《Sed 和 awk》這本書。
awk
編程進階的關鍵之一是掌握「擴展正則表達式」。awk
為你可能已經熟悉的 sed 正則表達式語法提供了幾個強大的補充。
另一個學習 awk
的好資源是 GNU awk 用戶指南。它有一個完整的 awk
內置函數庫的參考資料,以及很多簡單和複雜的 awk
腳本的例子。
via: https://opensource.com/article/19/10/advanced-awk
作者:Dave Neary 選題:lujun9972 譯者:wxy 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive