AWK教程

一. AWK入門指南

Awk是一種便於使用且表達能力強的程序設計語言,可應用於各種計算和數據處理任務。本章是個入門指南,讓你能夠儘快地開始編寫你自己的程序。第二章將描述整個語言,而剩下的章節將向你展示如何使用Awk來解決許多不同方面的問題。縱觀全書,我們儘量選擇了一些對你有用、有趣並且有指導意義的實例。

1.1 起步

有用的awk程序往往很簡短,僅僅一兩行。假設你有一個名爲 emp.data 的文件,其中包含員工的姓名、薪資(美元/小時)以及小時數,一個員工一行數據,如下所示:

Beth 4.00 0
Dan 3.75 0
kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18

現在你想打印出工作時間超過零小時的員工的姓名和工資(薪資乘以時間)。這種任務對於awk來說就是小菜一碟。輸入這個命令行就可以了::

awk '$3 >0 { print $1, $2 * $3 }' emp.data 

你應該會得到如下輸出:

Kathy 40
Mark 100
Mary 121
Susie 76.5

該命令行告訴系統執行引號內的awk程序,從輸入文件 emp.data 獲取程序所需的數據。引號內的部分是個完整的awk程序,包含單個模式-動作語句。模式 $3>0用於匹配第三列大於0的輸入行,動作:

{ print $1, $2 * $3 } 

打印每個匹配行的第一個字段以及第二第三字段的乘積。

如果你想打印出還沒工作過的員工的姓名,則輸入命令行::

awk '$3 == 0 { print $1 }' emp.data 

這裏,模式 $3 == 0 匹配第三個字段等於0的行,動作:

{ print $1 } 

打印該行的第一個字段。

當你閱讀本書時,應該嘗試執行與修改示例程序。大多數程序都很簡短,所以你能快速理解awk是如何工作的。在Unix系統上,以上兩個事務在終端裏看起來是這樣的:

$ awk ‘$3 > 0 { print $1, $2 * $3 }’ emp.data
Kathy 40
Mark 100
Mary 121
Susie 76.5
$ awk ‘$3 == 0 { print $1 }’ emp.data
Beth
Dan
$

行首的 $ 是系統提示符,也許在你的機器上不一樣。

AWK程序的結構

讓我們回頭看一下到底發生了什麼事情。上述的命令行中,引號之間的部分是awk編程語言寫就的程序。本章中的每個awk程序都是一個或多個模式-動作語句的序列:

pattern { action }
pattern { action }
...

awk的基本操作是一行一行地掃描輸入,搜索匹配任意程序中模式的行。詞語“匹配”的準確意義是視具體的模式而言,對於模式 $3 >0 來說,意思是“條件爲真”。

每個模式依次測試每個輸入行。對於匹配到行的模式,其對應的動作(也許包含多步)得到執行,然後讀取下一行並繼續匹配,直到所有的輸入讀取完畢。

上面的程序都是模式與動作的典型示例。:

$3 == 0 { print $1 } 

是單個模式-動作語句;對於第三個字段爲0的每行,打印其第一個字段。

模式-動作語句中的模式或動作(但不是同時兩者)都可以省略。如果某個模式沒有動作,例如::

$3 == 0 

那麼模式匹配到的每一行(即,對於該行,條件爲真)都會被打印出來。該程序會打印 emp.data 文件中第三個字段爲0的兩行

Beth 4.00 0
Dan 3.75 0

如果有個沒有模式的動作,例如::

{ print $1 } 

那麼這種情況下的動作會打印每個輸入行的第一列。

由於模式和動作兩者任一都是可選的,所以需要使用大括號包圍動作以區分於其他模式。

執行AWK程序

執行awk程序的方式有多種。你可以輸入如下形式的命令行::

awk 'program' input files 

從而在每個指定的輸入文件上執行這個program。例如,你可以輸入::

awk '$3 == 0 { print $1 }' file1 file2 

打印file1和file2文件中第三個字段爲0的每一行的第一個字段。

你可以省略命令行中的輸入文件,僅輸入::

awk 'program' 

這種情況下,awk會將program應用於你在終端中接着輸入的任意數據行,直到你輸入一個文件結束信號(Unix系統上爲control-d)。如下是Unix系統的一個會話示例:

$ awk ‘$3 == 0 { print $1 }’
Beth 4.00 0

Beth

Dan 3.75 0

Dan

Kathy 3.75 10
Kathy 3.75 0

Kathy

...

加粗的字符是計算機打印的。

這個動作非常便於嘗試awk:輸入你的程序,然後輸入數據,觀察發生了什麼。我們再次鼓勵你嘗試這些示例並進行改動。

注意命令行中的程序是用單引號包圍着的。這會防止shell解釋程序中 $ 這樣的字符,也允許程序的長度超過一行。

當程序比較短小(幾行的長度)的時候,這種約定會很方便。然而,如果程序較長,將程序寫到一個單獨的文件中會更加方便。假設存在程序 progfile ,輸入命令行::

awk -f progfile optional list of input files 

其中 -f 選項指示awk從指定文件中獲取程序。可以使用任意文件名替換 progfile 。

錯誤

如果你的awk程序存在錯誤,awk會給你一段診斷信息。例如,如果你打錯了大括號,如下所示::

awk '$3 == 0 [ print $1 }' emp.data 

你會得到如下信息:

awk: syntax error at source line 1
context is
$3 == 0 >>> [ <<<
extra }
missing ]
awk: bailing out at source line 1

“Syntax error”意味着在 >>> <<< 標記的地方檢測到語法錯誤。“Bailing out”意味着沒有試圖恢復。有時你會得到更多的幫助-關於錯誤是什麼,比如大括號或括弧不匹配。

因爲存在句法錯誤,awk就不會嘗試執行這個程序。然而,有些錯誤,直到你的程序被執行纔會檢測出來。例如,如果你試圖用零去除某個數,awk會在這個除法的地方停止處理並報告輸入行的行號以及在程序中的行號(這話是什麼意思?難道輸入行的行號是忽略空行後的行號?)。

1.2 簡單輸出

這一節接下來的部分包含了一些短小,典型的awk程序,基於操縱上文中提到的 emp.data 文件. 我們會簡單的解釋程序在做什麼,但這些例子主要是爲了介紹 awk 中常見的一些簡單有用的操作 – 打印字段, 選擇輸入, 轉換數據. 我們並 沒有展現 awk 程序能做的所有事情, 也並不打算深入的去探討例子中的一些細節. 但在你讀完這一節之後, 你將能夠完成一些簡單的任務, 並且你將發現在閱讀後 面章節的時候會變的容易的多.

我們通常只會列出程序部分, 而不是整個命令行. 在任何情況下, 程序都可以用 引號包含起來放到 awk 命令的地一個參數中運行, 就像上文中展示的那樣, 或者 把它放到一個文件中使用 awk 的 -f 參數調用它.

在 awk 中僅僅只有兩種數據類型: 數值 和 字符構成的字符串. emp.data 是 一個包含這類信息的典型文件 – 混合了被空格和(或)製表符分割的數字和詞語.

Awk 程序一次從輸入文件的中讀取一行內容並把它分割成一個個字段, 通常默認 情況下, 一個字段是一個不包含任何空格或製表符的連續字符序列. 當前輸入的 行中的地一個字段被稱做 $1, 第二個是 $2, 以此類推. 整個行的內容被定 義爲 $0. 每一行的字段數量可以不同.

通常, 我們要做的僅僅只是打印出每一行中的某些字段, 也許還要做一些計算. 這一節的程序基本上都是這種形式.

打印每一行

如果一個動作沒有任何模式, 這個動作會對所有輸入的行進行操作. print 語 句用來打印(輸出)當前輸入的行, 所以程序

{ print } 

會輸出所有輸入的內容到標準輸出. 由於 $0 表示整行,

{ print $0 } 

也會做一樣的事情.

打印特定字段

使用一個 print 語句可以在同一行中輸出不止一個字段. 下面的程序輸出了每 行輸入中的第一和第三個字段

{ print $1, $3 } 

使用 emp.data 作爲輸入, 它將會得到

Beth 0 Dan 0 Kathy 10 Mark 20 Mary 22 Susie 18 

在 print 語句中被逗號分割的表達式, 在默認情況下他們將會用一個空格分割 來輸出. 每一行 print 生成的內容都會以一個換行符作爲結束. 但這些默認行 爲都可以自定義; 我們將在第二章中介紹具體的方法.

NF, 字段數量

很顯然你可能會發現你總是需要通過 $1, $2 這樣來指定不同的字段, 但任何表 達式都可以使用在$之後來表達一個字段的序號; 表達式會被求值並用於表示字段 序號. Awk會對當前輸入的行有多少個字段進行計數, 並且將當前行的字段數量存 儲在一個內建的稱作 NF 的變量中. 因此, 下面的程序

{ print NF, $1, $NF } 

會依次打印出每一行的字段數量, 第一個字段的值, 最後一個字段的值.

計算和打印

你也可以對字段的值進行計算後再打印出來. 下面的程序

{ print $1, $2 * $3 } 

是一個典型的例子. 它會打印出姓名和員工的合計支出(以小時計算):

Beth 0 Dan 0 Kathy 40 Mark 100 Mary 121 Susie 76.5 

我們馬上就會學到怎麼讓這個輸出看起來更漂亮.

打印行號

Awk提供了另一個內建變量, 叫做 NR, 它會存儲當前已經讀取了多少行的計數. 我們可以使用 NR 和 $0 給 emp.data 的沒一行加上行號:

{ print NR, $0 } 

打印的輸出看起來會是這樣:

1 Beth 4.00 0 2 Dan 3.75 0 3 Kathy 4.00 10 4 Mark 5.00 20 5 Mary 5.50 22 6 Susie 4.25 1 8 

在輸出中添加內容

你當然也可以在字段中間或者計算的值中間打印輸出想要的內容:

{ print "total pay for", $1, "is", $2 * $3 } 

輸出

total pay for Beth is 0 total pay for Dan is 0 total pay for Kathy is 40 total pay for Mark is 100 total pay for Mary is 121 total pay for Susie is 76.5 

在打印語句中, 雙引號內的文字將會在字段和計算的值中插入輸出.

1.3 高級輸出

print 語句可用於快速而簡單的輸出。若要嚴格按照你所想的格式化輸出,則需要使用 printf 語句。正如我將在2.4節所見, printf 幾乎可以產生任何形式的輸出,但在本節中,我們僅展示其部分功能。

字段排隊

printf 語句的形式如下::

printf(format, value1, value2, ..., valuen) 

其中 format 是字符串,包含要逐字打印的文本,穿插着 format 之後的每個值該如何打印的規格(specification)。一個規格是一個 % 符,後面跟着一些字符,用來控制一個 value 的格式。第一個規格說明如何打印 value1 ,第二個說明如何打印 value2 ,... 。因此,有多少 value 要打印,在 format 中就要有多少個 %規格。

這裏有個程序使用 printf 打印每位員工的總薪酬::

{ printf("total pay for %s is $%.2f\n", $1, $2 * $3) } 

printf 語句中的規格字符串包含兩個 % 規格。第一個是 %s ,說明以字符串的方式打印第一個值 $1 。第二個是 %.2f ,說明以數字的方式打印第二個值 $2*$3 ,並保留小數點後面兩位。規格字符串中其他東西,包括美元符號,僅逐字打印。字符串尾部的 \n 代表開始新的一行,使得後續輸出將從下一行開始。以 emp.data爲輸入,該程序產生:

total pay for Beth is $0.00 total pay for Dan is $0.00 total pay for Kathy is $40.00 total pay for Mark is $100.00 total pay for Mary is $121.00 total pay for Susie is $76.50 

printf 不會自動產生空格或者新的行,必須是你自己來創建,所以不要忘了 \n 。

另一個程序是打印每位員工的姓名與薪酬::

{ printf("%-8s $%6.2f\n", $1, $2 * $3) } 

第一個規格 %-8s 將一個姓名以字符串形式在8個字符寬度的字段中左對齊輸出。第二個規格 %6.2f 將薪酬以數字的形式,保留小數點後兩位,在6個字符寬度的字段中輸出。

Beth $ 0.00 Dan $ 0.00 Kathy $ 40.00 Mark $100.00 Mary $121.00 Susie $ 76.50 

之後我們將展示更多的 printf 示例。一切精彩盡在2.4小節。

排序輸出

假設你想打印每位員工的所有數據,包括他或她的薪酬,並以薪酬遞增的方式進行排序輸出。最簡單的方式是使用awk將每位員工的總薪酬置於其記錄之前,然後利用一個排序程序來處理awk的輸出。Unix上,命令行如下:

awk '{ printf("%6.2f    %s\n", $2 * $3, $0) }' emp.data | sort 

將awk的輸出通過管道傳給 sort 命令,輸出爲:

  0.00 Beth 4.00 0 0.00 Dan 3.75 0 40.00 Kathy 4.00 10 76.50 Susie 4.25 18 100.00 Mark 5.00 20 121.00 Mary 5.50 22 

1.4 選擇

Awk的模式適合用於爲進一步的處理從輸入中選擇相關的數據行。由於不帶動作的模式會打印所有匹配模式的行,所以很多awk程序僅包含一個模式。本節將給出一些有用的模式示例。

通過對比選擇

這個程序使用一個對比模式來選擇每小時賺5美元或更多的員工記錄,也就是,第二個字段大於等於5的行::

$2 >= 5 

從 emp.data 中選出這些行::

Mark 5.00 20 Mary 5.50 22 

通過計算選擇

程序

$2 * $3 > 50 { printf("$%.2f for %s\n", $2 * $3, $1) } 

打印出總薪資超過50美元的員工的薪酬。

通過文本內容選擇

除了數值測試,你還可以選擇包含特定單詞或短語的輸入行。這個程序會打印所有第一個字段爲 Susie 的行::

$1 == "Susie" 

操作符 == 用於測試相等性。你也可以使用稱爲 正則表達式 的模式查找包含任意字母組合,單詞或短語的文本。這個程序打印任意位置包含 Susie 的行::

/Susie/ 

輸出爲這一行::

Susie 4.25 18 

正則表達式可用於指定複雜的多的模式;2.1節將會有全面的論述。

模式組合

可以使用括號和邏輯操作符與 && , 或 || , 以及非 ! 對模式進行組合。程序:

$2 >= 4 || $3 >= 20 

會打印 $2 (第二個字段) 大於等於 4 或者 $3 (第三個字段) 大於等於 20 的行::

Beth 4.00 0 kathy 4.00 10 Mark 5.00 20 Mary 5.50 22 Susie 4.25 18 

兩個條件都滿足的行僅打印一次。與如下包含兩個模式程序相比::

$2 >= 4 $3 >= 20 

如果某個輸入行兩個條件都滿足,這個程序會打印它兩遍::

Beth 4.00 0 Kathy 4.00 10 Mark 5.00 20 Mark 5.00 20 Mary 5.50 22 Mary 5.50 22 Susie 4.25 18 

注意如下程序:

!($2 < 4 && $3 < 20) 

會打印極不滿足 $2 小於4也不滿足 $3 小於20的行;這個條件與上面第一個模式組合等價,雖然也許可讀性差了點。

數據驗證

實際的數據中總是會存在錯誤的。在數據驗證-檢查數據的值是否合理以及格式是否正確-方面,Awk是個優秀的工具。

數據驗證本質上是否定的:不是打印具備期望屬性的行,而是打印可疑的行。如下程序使用對比模式 將5個數據合理性測試應用於 emp.data 的每一行::

NF != 3 { print $0, "number of fields is not equal to 3" } $2 < 3.35 { print $0, "rate is below minimum wage" } $2 > 10 { print $0, "rate exceeds $10 per hour" } $3 < 0 { print $0, "negative hours worked" } $3 > 60 { print $0, "too many hours worked" } 

如果沒有錯誤,則沒有輸出。

BEGIN與END

特殊模式 BEGIN 用於匹配第一個輸入文件的第一行之前的位置, END 則用於匹配處理過的最後一個文件的最後一行之後的位置。這個程序使用 BEGIN 來輸出一個標題::

BEGIN { print "Name    RATE    HOURS"; print ""} { print } 

輸出爲::

NAME RATE HOURS Beth 4.00 0 Dan 3.75 0 Kathy 4.00 10 Mark 5.00 20 Mary 5.50 22 Susie 4.25 18 

程序的動作部分你可以在一行上放多個語句,不過要使用分號進行分隔。注意 普通的 print 是打印當前輸入行,與之不同的是 print “” 會打印一個空行。

1.5 使用AWK進行計算

一個動作就是一個以新行或者分號分隔的語句序列。你已經見過一些其動作僅是單個 print 語句的例子。本節將提供一些執行簡單的數值以及字符串計算的語句示例。在這些語句中,你不僅可以使用像 NF 這樣的內置變量,還可以創建自己的變量用於計算、存儲數據諸如此類的操作。awk中,用戶創建的變量不需要聲明。

計數

這個程序使用一個變量 emp 來統計工作超過15個小時的員工的數目::

$3 > 15 { emp = emp + 1 } END { print emp, "employees worked more than 15 hours" } 

對於第三個字段超過15的每行, emp 的前一個值加1。以 emp.data 爲輸入,該程序產生::

3 employees worked more than 15 hours 

用作數字的awk變量的默認初始值爲0,所以我們不需要初始化 emp 。

求和與平均值

爲計算員工的數目,我們可以使用內置變量 NR ,它保存着到目前位置讀取的行數;在所有輸入的結尾它的值就是所讀的所有行數。

END { print NR, "employees" } 

輸出爲::

6 employees 

如下是一個使用 NR 來計算薪酬均值的程序::

    { pay = pay + $2 * $3 } END { print NR, "employees" print "total pay is", pay print "average pay is", pay/NR } 

第一個動作累計所有員工的總薪酬。 END 動作打印出

6 employees total pay is 337.5 average pay is 56.25 

很明顯, printf 可用來產生更簡潔的輸出。並且該程序也有個潛在的錯誤:在某種不太可能發生的情況下, NR 等於0,那麼程序會試圖執行零除,從而產生錯誤信息。

處理文本

awk的優勢之一是能像大多數語言處理數字一樣方便地處理字符串。awk變量可以保存數字也可以保存字符串。這個程序會找出時薪最高的員工::

$2 > maxrate { maxrate = $2; maxemp = $1 } END { print "highest hourly rate:", maxrate, "for", maxemp } 

輸出

highest hourly rate: 5.50 for Mary

這個程序中,變量 maxrate 保存着一個數值,而變量 maxemp 則是保存着一個字符串。(如果有幾個員工都有着相同的最大時薪,該程序則只找出第一個。)

字符串連接

可以合併老字符串來創建新字符串。這種操作稱爲 連接(concatenation) 。程序

    { names = names $1 " "} END { print names } 

通過將每個姓名和一個空格附加到變量 names 的前一個值, 來將所有員工的姓名收集進單個字符串中。最後 END 動作打印出 names 的值::

Beth Dan Kathy Mark Mary Susie 

awk程序中,連接操作的表現形式是將字符串值一個接一個地寫出來。對於每個輸入行,程序的第一個語句先連接三個字符串: names 的前一個值、當前行的第一個字段以及一個空格,然後將得到的字符串賦值給 names 。因此,讀取所有的輸入行之後, names 就是個字符串,包含所有員工的姓名,每個姓名後面跟着一個空格。用於保存字符串的變量的默認初始值是空字符串(也就是說該字符串包含零個字符),因此這個程序中的 names 不需要顯式初始化。

打印最後一個輸入行

雖然在 END 動作中 NR 還保留着它的值,但 $0 沒有。程序

    { last = $0 } END { print last } 

是打印最後一個輸入行的一種方式::

Susie 4.25 18 

內置函數

我們已看到awk提供了內置變量來保存某些頻繁使用的數量,比如:字段的數量和輸入行的數量。類似地,也有內置函數用來計算其他有用的數值。除了平方根、對數、隨機數諸如此類的算術函數,也有操作文本的函數。其中之一是 length ,計算一個字符串中的字符數量。例如,這個程序會計算每個人的姓名的長度::

{ print $1, length($1) } 

結果::

Beth 4 Dan 3 Kathy 5 Mark 4 Mary 4 Susie 5 

行、單詞以及字符的計數

這個程序使用了 length 、 NF 、以及 NR 來統計輸入中行、單詞以及字符的數量。爲了簡便,我們將每個字段看作一個單詞。

    { nc = nc + length($0) + 1 nw = nw + NF } END { print NR, "lines,", nw, "words,", nc, "characters" } 

文件 emp.data 有:

6 lines, 18 words, 77 characters 

$0 並不包含每個輸入行的末尾的換行符,所以我們要另外加個1。

1.6 控制語句

Awk爲選擇提供了一個 if-else 語句,以及爲循環提供了幾個語句,所以都效仿C語言中對應的控制語句。它們僅可以在動作中使用。

if-else語句

如下程序將計算時薪超過6美元的員工的總薪酬與平均薪酬。它使用一個 if 來防範計算平均薪酬時的零除問題。

$2 > 6 { n = n + 1; pay = pay + $2 * $3 } END { if (n > 0) print n, "employees, total pay is", pay, "average pay is", pay/n else print "no employees are paid more than $6/hour" } 

emp.data 的輸出是::

no employees are paid more than $6/hour 

if-else 語句中,if 後的條件會被計算。如果爲真,執行第一個 print 語句。否則,執行第二個 print 語句。注意我們可以使用一個逗號將一個長語句截斷爲多行來書寫。

while語句

一個 while 語句有一個條件和一個執行體。條件爲真時執行體中的語句會被重複執行。這個程序使用公式 value=amount(1+rate)years

來演示以特定的利率投資一定量的錢,其數值是如何隨着年數增長的。

# interest1 - 計算複利 #   輸入: 錢數    利率    年數 #   輸出: 複利值 { i = 1 while (i <= $3) { printf("\t%.2f\n", $1 * (1 + $2) ^ i) i = i + 1 } } 

條件是 while 後括弧包圍的表達式;循環體是條件後大括號包圍的兩個表達式。 printf 規格字符串中的 \t 代表製表符; ^ 是指數操作符。從 # 開始到行尾的文本是註釋,會被awk忽略,但能幫助程序的讀者理解程序做的事情。

你可以爲這程序輸入三個一組的數字,看看不一樣的錢數、利率、以及年數會產生什麼。例如,如下事務演示了1000美元,利率爲6%與12%,5年的複利分別是如何增長的::

$ awk -f interest1 1000 .06 5 1060.00 1123.60 1191.02 1262.48 1338.23 1000 .12 5 1120.00 1254.40 1404.93 1573.52 1762.34 

for語句

另一個語句, for ,將大多數循環都包含的初始化、測試、以及自增壓縮成一行。如下是之前利息計算的 for 版本::

# interest1 - 計算複利 #   輸入: 錢數    利率    年數 #   輸出: 每年末的複利 { for (i = 1; i <= $3; i = i + 1) printf("\t%.2f\n", $1 * (1 + $2) ^ i) } 

初始化 i = 1 只執行一次。接下來,測試條件 i <= $3 ;如果爲真,則執行循環體的 printf 語句。循環體執行結束後執行自增 i = i + 1 ,接着由另一次條件測試開始下一個循環迭代。代碼更加緊湊,並且由於循環體僅是一條語句,所以不需要大括號來包圍它。

1.7 數組

awk爲存儲一組相關的值提供了數組。雖然數組給予了awk很強的能力,但在這裏我們僅展示一個簡單的例子。如下程序將按行逆序打印輸入。第一個動作將輸入行存爲數組 line 的連續元素;即第一行放在 line[1] ,第二行放在 line[2] , 依次繼續。 END 動作使用一個 while 語句從後往前打印數組中的輸入行::

# 反轉 - 按行逆序打印輸入 { line[NR] = $0 } # 記下每個輸入行 END { i = NR # 逆序打印 while (i > 0) { print line[i] i = i - 1 } } 

以 emp.data 爲輸入,輸出爲

Susie 4.25 18 Mary 5.50 22 Mark 5.00 20 Kathy 4.00 10 Dan 3.75 0 Beth 4.00 0 

如下是使用 for 語句實現的相同示例::

# 反轉 - 按行逆序打印輸入 { line[NR] = $0 } # 記下每個輸入行 END { for (i = NR; i > 0; i = i - 1) print line[i] }
 

二. AWK語言詳解

本章將主要通過示例來解釋構成awk程序的概念。因爲這是對語言的全面描述,材料會很詳細,因此我們推薦你瀏覽略讀,需要的時候再回來覈對細節。

最簡單的awk程序就是一個模式-動作語句的序列::

pattern { action } pattern { action } ... 

某些語句中,可能沒有模式;另一些語句中,可能沒有動作及其大括號。awk檢查你的程序以確認不存在語法錯誤後,一次讀取一行輸入,並對每一行按序處理模式。對於每個匹配到當前輸入行的模式,執行其關聯的動作。不存在模式,則匹配每個輸入行,因此沒有模式的每個動作對於每個輸入行都要執行。一個僅包含模式的模式-動作語句將打印匹配該模式的每個輸入行。本章的大部分內容中,名詞”輸入行(input-line)”和”記錄(record)” 是同義的。2.5小節中,我們將討論多行記錄,即一個記錄包含多行輸入。

本章的第一節將詳細描述模式。第二節通過表達式、賦值以及控制語句來描述動作。剩下的章節覆蓋函數定義,輸出,輸入,以及awk程序如何調用其他程序等內容。多數章節包含了主要特性的概要。

輸入文件 countries

本章中,我們使用一個名爲 countries 的文件作爲許多awk程序的輸入。文件的每行包含一個國家的名字,以千平方英里爲單位的面積,以百萬爲單位的人口數,以及屬於哪個洲。數據是1984年的,蘇聯(USSR)被武斷地歸入了亞洲。文件中,四列數據以製表符tab分隔;以單個空格將 North 、 South 與 America 分隔開。

文件 countries 包含如下數據行::

USSR 8649 275 Asia Canada 3852 25 North America China 3705 1032 Asia USA 3615 237 North America Brazil 3286 134 South America India 1267 746 Asia Mexico 762 78 North America France 211 55 Europe Japan 144 120 Asia Germany 96 61 Europe England 94 56 Europe 

本章的其餘部分,如果沒有明確說明輸入文件,那麼就是使用文件 countries 。

程序的格式

模式-動作語句以及動作中的語句通常以換行分隔,如果它們以分號分隔,則多個語句可以出現在一行中。分號可以放在任意語句的尾部。

動作的開大括號必須與其對應的模式處於同一行;動作的其餘部分,包括閉大括號,則可以出現接下來的行中。

空行會被忽略;一般爲了提高程序的可讀性會在語句的前面或者後面插入空行。在操作符和操作數的兩邊插入空格和製表符也是爲了提高可讀性。

任意行的末尾可能會有註釋。註釋以符號 # 開始,結束於行尾,就像這樣

{ print $1, $3 } # print country name and population 

長語句可以跨越多行,但要在斷行的地方加入一個反斜槓和一個換行符::

{ print \
                $1,             # country name
                $2,             # area in thousands of square miles
                $3 }    # population in millions

如上例所示,語句也可以逗號斷行,在每個斷行的末尾也可以加入註釋。

本書中,我們使用了多種格式風格,部分是爲了說明相異之處,部分是爲了避免程序佔用太多的行空間。類似於本章中的簡短程序,格式並不是很重要,但一致性與可讀性可以幫助更長的程序保持可控。

2.1 模式

模式控制着動作的執行:模式匹配,其關聯的動作則執行。本節將描述6種模式及其匹配條件。


模式摘要

1. BEGIN { 語句 }
在讀取任何輸入前執行一次 語句
2. END { 語句 }
讀取所有輸入之後執行一次 語句
3. 表達式 { 語句 }
對於 表達式 爲真(即,非零或非空)的行,執行 語句
4. /正則表達式/ { 語句 }
如果輸入行包含字符串與 正則表達式 相匹配,則執行 語句
5. 組合模式 { 語句 }
一個 組合模式 通過與(&&),或(||),非(|),以及括弧來組合多個表達式;對於組合模式爲真的每個輸入行,執行 語句
6. 模式1,模式2 { 語句 }
範圍模式(range pattern)匹配從與 模式1 相匹配的行到與 模式2 相匹配的行(包含該行)之間的所有行,對於這些輸入行,執行 語句 。

BEGIN和END不與其他模式組合。範圍模式不可以是任何其他模式的一部分。BEGIN和END是僅有的必須搭配動作的模式。




awk '{print}' /etc/passwd              :打印/etc/passwd所有內容

awk -F : '{print $1}' /etc/passwd                 :以 ":" 爲分隔符 ,打印/etc/passwd第一字段,也就是用戶名

awk -F : '{print $1 " " $2}' /etc/passwd                 :同上功能,打出第1,第二字段。  中間加個空格輸出。

awk 'BEGIN{FS=":";OFS="\t";ORS="\n"}{print $1,$2}' /etc/passwd ***  FS:字段分隔符

    OFS:輸出字段分隔符

    ORS:輸出記錄分隔符

以上命令爲以 : 爲分隔符,每個字段用\t隔開,輸出一行後\n (換行), 輸出第1,2字段。中間用,隔開

awk '{print NR}' /etc/passwd                 :讀取/etc/passwd的每一行行號  (NR至今的讀取記錄數)

awk 'END{print NR}' /etc/passwd                  : 加了個END,讀最後一個讀取記錄數的數字! 輸出結果:29 表示29個用戶

awk '/root/' /etc/passwd                   :輸出 /etc/passwd裏包含  root 這個字眼的 記錄!打印出來 。'/ /'裏表示的是符合字符

awk '{if($1~/root/) print $0}' /etc/passwd             : 如果第一個字段裏有符合root字眼的,打印當前行,$0表示當前行(當前記錄) awk '/^[Rr]oot/' /etc/passwd               :   '//'裏表示匹配字符  ^表示匹配第一個字母 [Rr]表示這個字母是R或者r也可以

                                    所以上面的語句表示:第一個字母符合Root或者root就打印改記錄條

awk '$2~/^..u/' /etc/passwd             : ~/表示匹配,這裏表示第2個字段第3個字母爲u的話,就打印出該行

awk '/(lin|root)/' /etc/passwd         :記錄裏有匹配 lin或者root的!就打印出來

echo ababab | awk 'gsub(/a/,"c")'           : 輸出  ababab  再把輸出結果交給awk處理,最後吧 a換成了c .輸出爲 cbcbcb

awk '{if($4!~"301|302") print $0}' filename           :要是 第4個字段, 不匹配  301或者 302!就打印出該記錄  !~表示不匹配,~表示匹配

awk '$1!~/^.-/'  不匹配第2個字符是 -

cat XXX.txt |grep -v "^#"|grep -v "^$" > XXX.txt  把開頭是#的!全部刪掉

cat t.log |awk '{if($3!~/note/) print $0}'|grep "2010-11-14"|wc -l  不匹配note

cat t.log |awk '{if($3~/note/) print $0}'|grep "2010-11-14"|wc -l  匹配note

cat t.log |grep -E "2010-11-14|2010-11-15" |more

cat t.log |grep -E "2010-11-14|2010-11-15" |awk '{if($3~/test/) print $0}' |more

cat /etc/passwd |cut -d ":" -f1 |grep root

cat tmp |awk '{if($3~/paijiang/ && $1~/2010-11-14/) print $0}'|wc -l

ps aux| awk '{print $2}'

cat /proc/meminfo  | awk '/MemTotal/{print $2}' 內存大小

df -h | awk '{print $5}' |grep -v 'User%'    grep -v 是刪除包含 User% 的行

常用的作用格式:

awk "樣式" 文件

awk "{操作}" 文件

awk "樣式{操作}" 文件

awk是一種用於處理數據和生成報告的編程語言。

一般的使用格式是:

   awk '{pattern + action}' {filenames} pattern指在每一行中進行匹配的條件,action指針對符合條件的行進行的操作,filenames是輸入的文件名。 假設data文件中有以下數據:

   1 donald 305 20050129

   2 chin 102 20040129

   3 mark 304 20040229

下面對這個文件進行操作:

   awk '{print $1, $2, $3, $4}' data

而$1,$2...則相應代表分割後的列

逗號則讓列與列之間插入空格

輸出:

   1 donald 305 20050129

   2 chin 102 20040129

   3 mark 304 20040229

------------條件操作符

1 匹配 ~

awk ‘{if($1~/root/) print $0}’ /etc/passwd    #如果field1包含"root",打印該行

2.精確匹配

!=  ==

3.不匹配

!~

4.大小比較

> >= < <=

5.設置大小寫 awk ‘/^[Rr]oot/’ /etc/passwd # 打印包含行首爲Root或者root的行

6.任意字符 .

awk ‘$2~/^…a/’ /etc/passwd # 打印第二個字段開頭第四個字母爲t的行

7.或關係匹配 |

awk ‘/(root|ftp)/’ /etc/passwd #打印包含"root"或者"ftp"的行

8.AND &&    OR  ||

awk ‘{$1~/mail/ && $7==/bin/bash}’ /etc/passwd

-----------------系統變量:

   ARGV     命令行參數數組

   ENVIRON     環境變量數組

   FILENAME     當前輸入文件名

   FNR     當前文件中的記錄號

   FS     字段分隔符

   IGNORECASE     忽略正則表達式和串的大小寫

   NF     當前記錄中的字段數     NR     至今讀取的記錄數     OFMT     數的輸出格式,缺省爲"%.6g"

   OFS     輸出字段分隔符

   ORS     輸出記錄分隔符

   RS     輸入記錄分隔符     RSTART     由match() 匹配的第一個字符的索引

   RLENGTH     由match() 匹配的串的長度

   SUBSEP     下標分隔符,缺省爲"�34"

    內置字符串函數

   gsub(r,s,t)     在字符串t中,用字符串s替換和正則表達式r匹配的所有字符串。返回替換的個數。如果沒有給出t,缺省爲$0

   index(s,t)       返回s 中字符串t 的位置,不出現時爲0

   length(s)     返回字符串s 的長度,當沒有給出s時,返回$0的長度

   match(s,r)     返回r 在s 中出現的位置,不出現時爲0。設置RSTART和RLENGTH的值

   split(s,a,r)     利用r 把s 分裂成數組a,返回元素的個數。如果沒有給出r,則使用FS。數組分割和字段分割採用同樣的方式

   sprintf(fmt,expr_list)       根據格式串fmt,返回經過格式編排的expr_list

   sub(r,s,t)     在字符串t中用s替換正則表達式t的首次匹配。如果成功則返回1,否則返回0。如果沒有給出t,默認爲$0

   substr(s,p,n)     返回字符串s中從位置p開始最大長度爲n的字串。如果沒有給出n,返回從p開始剩餘的字符串

   tolower(s)     將串s 中的大寫字母改爲小寫,返回新串

   toupper(s)     將串s 中的小寫字母改爲大寫,返回新串

-----------------------awk的環境變量

變量   描述 $n   當前記錄的第n個字段,字段間由FS分隔 。

$0   完整的輸入記錄。

ARGC   命令行參數的數目。

ARGIND   命令行中當前文件的位置(從0開始算)。

ARGV   包含命令行參數的數組。

CONVFMT   數字轉換格式(默認值爲%.6g)

ENVIRON   環境變量關聯數組。

ERRNO   最後一個系統錯誤的描述。

FIELDWIDTHS   字段寬度列表(用空格鍵分隔)。

FILENAME   當前文件名。

FNR   同NR,但相對於當前文件。

FS   字段分隔符(默認是任何空格)。

IGNORECASE   如果爲真,則進行忽略大小寫的匹配。

NF   當前記錄中的字段數。 NR   當前記錄數。 OFMT   數字的輸出格式(默認值是%.6g)。

OFS   輸出字段分隔符(默認值是一個空格)。

ORS   輸出記錄分隔符(默認值是一個換行符)。

RLENGTH   由match函數所匹配的字符串的長度。

RS   記錄分隔符(默認是一個換行符)。

RSTART   由match函數所匹配的字符串的第一個位置。

SUBSEP   數組下標分隔符(默認值是\034)。

----------------------------------gawk專用正則表達式元字符

一般通用的元字符集就不講了,可參考我的Sed和Grep學習筆記。以下幾個是gawk專用的,不適合unix版本的awk。

\Y

匹配一個單詞開頭或者末尾的空字符串。

\B

匹配單詞內的空字符串。

\<

匹配一個單詞的開頭的空字符串,錨定開始。

\>

匹配一個單詞的末尾的空字符串,錨定末尾。

\w

匹配一個字母數字組成的單詞。

\W

匹配一個非字母數字組成的單詞。

\‘

匹配字符串開頭的一個空字符串。

\'

匹配字符串末尾的一個空字符串。

--------------------------------------------

 awk '/donald/ {print $4}' data       匹配當data文件中包含字符串"donald"的行,輸出第4列的值:

 awk '/donald|chin/ {print $1, $2}' data      這裏的"|"應該是或的作用,而不是管道,輸出:

   1 donald

   2 chin

   awk '/a[rl]/ {print $1, $2}' data    兼容perl的正則表達式,匹配包含"ar"或"al"的列 ,輸出:

   1 donald

   3 mark

   awk '/a[rl]/ {print $1, $2, $3+1}' data       給第三列加上1再輸出:     1 donald 306

   3 mark 305

   awk '/a[rl]/ {print $1, $2} {print $3+1}' data     匹配只對第一對花括號產生作用 ,輸出:

   1 donald

   306

   103

   3 mark

   305

   awk 'FS="n" {print $1}' data   使用"n"而不是空格做爲分隔符,輸出:

   1

   2 chi

   3 mark 304 20040229

   awk 'FS="n" {OFS="-"} {print $1, $2}' data

把分隔符輸出:

   1-donald

   2 chi- 102 20040129

   3 mark 304 20040229-

   awk 'FS="n" {OFS="-"} {print NR, $1, $2}' data

使用NR變量,num of row,即行號,輸出:

   1-1-donald

   2-2 chi- 102 20040129

   3-3 mark 304 20040229-

   awk '{x=x+$3} {print NR, $3, x}' data

使用變量進行累加計算,輸出:

   1 305 305

   2 102 407

   3 304 711

   awk '{x=x+$3} END {print NR, $3, x}' data

使用BEGIN和END在處理之前或之後顯示結果,輸出:

   3 304 711

   awk '{x=x+$3} {print NR, $3, x | "sort -nr"}' data 在awk內使用管道進行排序,輸出:

   3 304 711

   2 102 407

   1 305 305

   cat command

   {x=x+$3}

   {print NR, $3, x | "sort -nr"}

   awk -f command data

將指定寫在文件中,輸出:

   3 304 711

   2 102 407

   1 305 305

$awk '$2 == "chin" { print $3 }' 1.txt

如果簡單的輸出不能處理您的程序中所需要的複雜信息,則可以嘗試由 printf 命令獲得的更加複雜的輸出,其語法是

printf( format, value, value ...) 該語法類似於 C 語言中的 printf 命令,而格式的規格是相同的。通過插入一項定義如何打印數值的規格,可以定義該格式。格式規格包含一個跟有字母的 %。類似於打印命令,printf 不必包含在圓括號中,但是可以認爲使用圓括號是一種良好的習慣。

下表列出 printf 命令提供的各種規格。

規格 說明

%c 打印單個 ASCII 字符

%d 打印十進制數

%e 打印數字的科學計數表示

%f 打印浮點表示

%g 打印 %e 或 %f;兩種方式都更簡短

%o 打印無符號的八進制數

s 打印 ASCII 字符串

%x 打印無符號的十六進制數

%% 打印百分號;不執行轉換

可以在 % 與字符之間提供某些附加的格式化參數。這些參數進一步改進數值的打印方式:

參數 說明

- 將字段中的表達式向左對齊

,width 根據需要將字段補齊到指定寬度(前導零使用零將字段補齊)

.prec 小數點右面數字的最大字符串寬度或最大數量

====================

文件disk.txt:

disk        used        noused    sum       0        209715        2097152   2306867

1        209715        2097152   2306867

2        209715        2097152   2306867

3        209715        2097152  2306867

現在我想把這個文檔處理成這樣

硬盤        已使用        未使用     默認大小  //英文改成中文

0        0.2G        2G         2.2G     //209715k換算成G 1M=1024k 1G=1024M 並且限制小數位爲1,後面加上單位名G

1        0.2G        2G         2.2G

2        0.2G        2G         2.2G

3        0.2G        2G         2.2G ======》》

awk '{if (NR==1) {print "硬盤\t已使用\t未使用\t默認大小"} else {u=$2/1024/1024; n=$3/1024/1024; s=$4/1024/1024; p=$2/$4*100; printf("%d\t%.1fG(%.0f%)\t%.1fG\t%.1fG\n",$1,u,p,n,s)}}' disk.txt

============================

$cat file

user1  par11   par12

user2  par21   par22

user1  par31   par32

awk '{if($1=="'user1'") print $1,$2,$3}' file

這樣會把結果user1  par11   par12

                 user1  par31   par32   都輸出來

我想當$1匹配到第一個user1的時候,就輸出結果。

=====>>>>

awk '{if($1=="'user1'") {print $1,$2,$3;exit}}' file

$ awk '{ print $0 }' /etc/passwd

在 Awk 中,變量 $0 表示整個當前行,因此 print 和 print $0 的作用完全相同。

$awk -F":" '{ print $1 $3 }' /etc/passwd halt7

operator11

root0

shutdown6

sync5

bin1

$awk -F":" '{ print "username: " $1 "\t\tuid:" $3 }' /etc/passwd

如果第一個域大於第三個域,$1就賦值給max,否則$3就賦值給max。

$ awk '{max = {$1 > $3} ? $1: $3: print max}' test

將顯示test文件第一列中以root開頭的行。

$ awk '$1 ~/^root/' test

如果第一和第二個域相加大於100,則打印這些行。

$ awk '$1 + $2 < 100' test。

awk '$1 > 5 && $2 < 10' test,如果第一個域大於5,並且第二個域小於10,則打印這些行。

$ awk '/root/,/mysql/' test將顯示root第一次出現到mysql第一次出現之間的所有行。

============12. 一個驗證passwd文件有效性的例子

1$ cat /etc/passwd | awk -F: '\

2NF != 7{\

3printf("line %d,does not have 7 fields:%s\n",NR,$0)}\

4$1 !~ /[A-Za-z0-9]/{printf("line %d,non alpha and numeric user id:%d: %s\n,NR,$0)}\

5$2 == "*" {printf("line %d, no password: %s\n",NR,$0)}'

1   cat把結果輸出給awk,awk把域之間的分隔符設爲冒號。

2   如果域的數量(NF)不等於7,就執行下面的程序。

3   printf打印字符串"line ?? does not have 7 fields",並顯示該條記錄。

4   如果第一個域沒有包含任何字母和數字,printf打印“no alpha and numeric user id" ,並顯示記錄數和記錄。

5   如果第二個域是一個星號,就打印字符串“no passwd”,緊跟着顯示記錄數和記錄本身。

$ awk '/^(no|so)/' test-----打印所有以模式no或so開頭的行。

*

$ awk '/^[ns]/{print $1}' test-----如果記錄以n或s開頭,就打印這個記錄。

*

$ awk '$1 ~/[0-9][0-9]$/(print $1}' test-----如果第一個域以兩個數字結束就打印這個記錄。

*

$ awk '$1 == 100 || $2 < 50' test-----如果第一個或等於100或者第二個域小於50,則打印該行。

*

$ awk '$1 != 10' test-----如果第一個域不等於10就打印該行。

*

$ awk '/test/{print $1 + 10}' test-----如果記錄包含正則表達式test,則第一個域加10並打印出來。

*

$ awk '{print ($1 > 5 ? "ok "$1: "error"$1)}' test-----如果第一個域大於5則打印問號後面的表達式值,否則打印冒號後面的表達式值。

*

$ awk '/^root/,/^mysql/' test----打印以正則表達式root開頭的記錄到以正則表達式mysql開頭的記錄範圍內的所有記錄。如果找到一個新的正則表達式root開頭的記 錄,則繼續打印直到下一個以正則表達式mysql開頭的記錄爲止,或到文件末尾。

awk '$1 ~/test/{count = $2 + $3; print count}' test,上式的作用是,awk先掃描第一個域,一旦test匹配,就把第二個域的值加上第三個域的值,並把結果賦值給變量count,最後打印出來。

$ awk '{if ($1 <$2) print $2 "too high"}' test。如果第一個域小於第二個域則打印。

$ awk '{if ($1 < $2) {count++; print "ok"}}' test.如果第一個域小於第二個域,則count加一,並打印ok。

$ awk '{if ($1 > 100) print $1 "bad" ; else print "ok"}' test。如果$1大於100則打印$1 bad,否則打印ok。

$ awk '{if ($1 > 100){ count++; print $1} else {count--; print $2}' test。如果$1大於100,則count加一,並打印$1,否則count減一,並打印$1。

====================awk有三種循環:while循環;for循環;special for循環。 *

$ awk '{ i = 1; while ( i <= NF ) { print NF,$i; i++}}' test。變量的初始值爲1,若i小於可等於NF(記錄中域的個數),則執行打印語句,且i增加1。直到i的值大於NF.

*

$ awk '{for (i = 1; i<NF; i++) print NF,$i}' test。作用同上。

*

breadkcontinue語句。break用於在滿足條件的情況下跳出循環;continue用於在滿足條件的情況下忽略後面的語句,直接返回循環的頂端。如:

{for ( x=3; x<=NF; x++)

if ($x<0){print "Bottomed out!"; break}}

{for ( x=3; x<=NF; x++)

if ($x==0){print "Get next item"; continue}}

*

next語句從輸入文件中讀取一行,然後從頭開始執行awk腳本。如:

{if ($1 ~/test/){next}

else {print}

}

*

exit語句用於結束awk程序,但不會略過END塊。退出狀態爲0代表成功,非零值表示出錯。

--------------------

1 、 awk '/101/' file 顯示文件 file 中包含 101 的匹配行。  IXDBA.NET技術社區  awk '/101/,/105/' file awk '$1 == 5' file awk '$1 == "CT"' file 注意必須帶雙引號  awk '$1 * $2 >100 ' file awk '$2 >5 && $2<=15' file  2 、 awk '{print NR,NF,$1,$NF,}' file 顯示文件 file 的當前記錄號、域數和每一行的第一個和最後一個域。  awk '/101/ {print $1,$2 + 10}' file 顯示文件 file 的匹配行的第一、二個域加 10 。  awk '/101/ {print $1$2}' file awk '/101/ {print $1 $2}' file 顯示文件 file 的匹配行的第一、二個域,但顯示時域中間沒有分隔符。  3 、 df | awk '$4>1000000 '  通過管道符獲得輸入,如:顯示第 4 個域滿足條件的行。  4 、 awk -F "|" '{print $1}' file  按照新的分隔符 “|” 進行操作。  awk 'BEGIN { FS="[: \t|]" } {print $1,$2,$3}' file  通過設置輸入分隔符( FS="[: \t|]" )修改輸入分隔符。  Sep="|" awk -F $Sep '{print $1}' file 按照環境變量 Sep 的值做爲分隔符。  awk -F '[ :\t|]' '{print $1}' file 按照正則表達式的值做爲分隔符,這裏代表空格、 : 、 TAB 、 | 同時做爲分隔符。  awk -F '[][]' '{print $1}' file 按照正則表達式的值做爲分隔符,這裏代表 [ 、 ]  5 、 awk -f awkfile file 通過文件 awkfile 的內容依次進行控制。  cat awkfile /101/{print "\047 Hello! \047"} -- 遇到匹配行以後打印 ' Hello! '.\047 代表單引號。  {print $1,$2} -- 因爲沒有模式控制,打印每一行的前兩個域。  6 、 awk '$1 ~ /101/ {print $1}' file  顯示文件中第一個域匹配 101 的行(記錄)。  7 、 awk 'BEGIN { OFS="%"} {print $1,$2}' file  通過設置輸出分隔符( OFS="%" )修改輸出格式。  8 、 awk 'BEGIN { max=100 ;print "max=" max} BEGIN  表示在處理任意行之前進行的操作。  {max=($1 >max ?$1:max); print $1,"Now max is "max}' file  取得文件第一個域的最大值。  (表達式 1? 表達式 2: 表達式 3 相當於:  if ( 表達式 1) 表達式 2 else 表達式 3 awk '{print ($1>4 ? "high "$1: "low "$1)}' file 9 、 awk '$1 * $2 >100 {print $1}' file  顯示文件中第一個域匹配 101 的行(記錄)。  10 、 awk '{$1 == 'Chi' {$3 = 'China'; print}' file  找到匹配行後先將第 3 個域替換後再顯示該行(記錄)。  awk '{$7 %= 3; print $7}' file  將第 7 域被 3 除,並將餘數賦給第 7 域再打印。  11 、 awk '/tom/ {wage=$2+$3; printf wage}' file  找到匹配行後爲變量 wage 賦值並打印該變量。  12 、 awk '/tom/ {count++;} END {print "tom was found "count" times"}' file END  表示在所有輸入行處理完後進行處理。  13 、 awk 'gsub(/\$/,"");gsub(/,/,""); cost+=$4; END {print "The total is $" cost> "filename"}' file  gsub 函數用空串替換 $ 和 , 再將結果輸出到 filename 中。  1 2 3 $1,200.00 1 2 3 $2,300.00 1 2 3 $4,000.00 awk '{gsub(/\$/,"");gsub(/,/,""); if ($4>1000&&$4<2000) c1+=$4; else if ($4>2000&&$4<3000) c2+=$4; else if ($4>3000&&$4<4000) c3+=$4; else c4+=$4; } END {printf "c1=[%d];c2=[%d];c3=[%d];c4=[%d]\n",c1,c2,c3,c4}"' file 通過 if 和 else if 完成條件語句  awk '{gsub(/\$/,"");gsub(/,/,""); if ($4>3000&&$4<4000) exit; else c4+=$4; } END {printf "c1=[%d];c2=[%d];c3=[%d];c4=[%d]\n",c1,c2,c3,c4}"' file 通過 exit 在某條件時退出,但是仍執行 END 操作。  awk '{gsub(/\$/,"");gsub(/,/,""); if ($4>3000) next; else c4+=$4; } END {printf "c4=[%d]\n",c4}"' file  通過 next 在某條件時跳過該行,對下一行執行操作。  14 、 awk '{ print FILENAME,$0 }' file1 file2 file3>fileall  把 file1 、 file2 、 file3 的文件內容全部寫到 fileall 中,格式爲打印文件並前置文件名。  15 、 awk ' $1!=previous { close(previous); previous=$1 } {print substr($0,index($0," ") +1)>$1}' fileall   把合併後的文件重新分拆爲 3 個文件。並與原文件一致。  16 、 awk 'BEGIN {"date"|getline d; print d}'  通過管道把 date 的執行結果送給 getline ,並賦給變量 d ,然後打印。  17 、 awk 'BEGIN {system("echo \"Input your name:\\c\""); getline d;print "\nYour name is",d,"\b!\n"}'  通過 getline 命令交互輸入 name ,並顯示出來。  awk 'BEGIN {FS=":";while(getline< "/etc/passwd" >0) { if($1~"050[0-9]_") print $1}}'  打印 /etc/passwd 文件中用戶名包含 050x_ 的用戶名。  18 、 awk '{ i=1;while(i  awk '{ for(i=1;i  type file|awk -F "/" ' { for(i=1;i

{ if(i==NF-1) { printf "%s",$i } else { printf "%s/",$i } })'  顯示一個文件的全路徑。   用 for 和 if 顯示日期  awk 'BEGIN { for(j=1;j<=12;j++) { flag=0; printf "\n%d 月份 \n",j; for(i=1;i<=31;i++) { if (j==2&&i>28) flag=1; if ((j==4||j==6||j==9||j==11)&&i>30) flag=1; if (flag==0) {printf "dd ",j,i} } } }' 19 、在 awk 中調用系統變量必須用單引號,如果是雙引號,則表示字符串  Flag=abcd awk '{print '$Flag'}' 結果爲 abcd awk '{print "$Flag"}' 結果爲 $Flag

---------------------------------awk應用小結(所有命令行均經調試)

1.調用awk: 第一種方式:命令行方式 awk [-F field-separator] 'commands' input-file(s) [-F域分隔符]是可選的,因爲awk使用空格作爲缺省的域分隔符,因此如果要瀏覽域間有空格的文本,不必指定這個選項,如果要瀏覽諸如passwd文件,此文件各域以冒號作爲分隔符,則必須指明-F選項,如: awk -F: 'commands' input-file 第二種方式是將所有awk命令插入一個文件,並使awk程序可執行,然後用awk命令解釋器作爲腳本的首行,以便通過鍵入腳本名稱來調用它。

第三種方式是將所有的awk命令插入一個單獨文件,然後調用: awk -f awk-scrīpt-file input-files(s) -f選項指明在文件awk_scrīpt_file中的awk腳本,input_file(s)是使用awk進行瀏覽的文件名。 模式和動作 任何awk語句都由模式和動作組成。在一個awk腳本中可能有許多語句。模式部分決定動作語句何時觸發及觸發事件。處理即對數據進行的操作。如果省略模式 部分,動作將時刻保持執行狀態。模式可以是任何條件語句或複合語句或正則表達式。模式包括兩個特殊字段 BEGIN和END。使用BEGIN語句設置計數和打印頭。BEGIN語句使用在任何文本瀏覽動作之前,之後文本瀏覽動作依據輸入文本開始執行。END語 句用來在awk完成文本瀏覽動作後打印輸出文本總數和結尾狀態標誌。 域和記錄 使用$1,$3表示參照第1和第3域,注意這裏用逗號做域分隔。如果希望打印一個有5個域的記錄的所有域,可使用$0,意即所有域。

爲打印一個域或所有域,使用print命令。這是一個awk動作 模式和動作 模式 :兩個特殊斷 BEGIN和END 動作 :實際動作大多在{}內指明 輸出 1.抽取域 awk -F: '{print $1}' /etc/passwd 2.保存輸出 awk -F: '{print $1}' /etc/passwd | tee user 使用tee命令,在輸出文件的同時,輸出到屏幕 使用標準輸出 awk -F: '{print $1}' /etc/passwd >user3 4.打印所有記錄 awk -F: '{print $0}' /etc/passwd 5.打印單獨記錄 awk -F: '{print $1,$4}' /etc/passwd 6.打印報告頭 awk -F: 'BEGIN {print "NAME\n"} {print $1}' /etc/passwd 7.打印結尾 awk -F: '{print $1} END {print "this is all users"}' /etc/passwd 條件操作符 1.匹配 awk -F: '{if($1~/root/) print }' /etc/passwd      //{if($1~/root/) print}表示如果field1包含root,打印它 2.精確匹配 使用等號 ==

awk -F: '{if($3=="0") print }' /etc/passwd  3.不匹配 !~

awk -F: '{if($1!~/linuxtone/) print }' /etc/passwd

!= 精確不匹配 awk -F: '{if($1!="linuxtone") print }' /etc/passwd 4.小於 < 5.小於等於 <= 6.大於 >

......... 7.設置大小寫 awk '/[Rr]oot/' /etc/passwd 8.任意字符 awk -F: '{if($1~/^...t/) print}' /etc/passwd      //^...t表示第四個字幕是t 9.或關係匹配 awk -F: '{if ($1~/(squid|nagios)/) print}' /etc/passwd 10.行首 awk '/^root/' /etc/passwd       // ^root(行首包含root) 11.AND && awk -F: '{if($1=="root" && $3=="0") print}' /etc/passwd 12.OR || 內置變量: ARCC                                      命令行參數個數 ARGV                                      命令行參數排列 ENVIRON                                   支持隊列中系統環境變量的使用 FNR                                       瀏覽文件的記錄數 FS                                        置頂分隔符,等價於-F NF                                        瀏覽記錄的域的個數 NR                                        已讀的記錄數 OFS                                       輸出域分隔符 ORS                                       輸出記錄分隔符 RS                                        控制記錄分隔符 打印有多少行記錄 awk 'END {print NR}' /etc/passwd 設置輸入域到變量名 awk -F: '{name=$1; path=$7; if(name~/root/) print name  "\tpath is:" path}' /etc/passwd 域值比較操作 awk '{if($6<$7) print $0}' input-file 修改文本域只顯示修改改記錄 awk -F: '{if($1=="nagios") {$1="nagios server" ; print }}' /etc/passwd 文件長度相加 ls -l|awk '/^[^d]/ {print $9"\t" $5} {tot+=$5} END {print "total kb:"tot}' 內置的字符竄函數 gsub(r,s)                                                      在整個$0中用s替代r gsub(r,s,t)                                                    在整個t中用s替代r index(s,t)                                                     返回s中字符串t的第一位置 length(s)                                                      返回s長度 match(s,r)                                                     測試s中是否包含匹配r的字符串 split(s,a,fs)                                                  在fs上將s分成序列a sub(s, )                                                       用$0中最左邊也是最長的字符串替代 subtr(s,p)                                                     返回字符串s中從p開始的後綴部分 substr(s,p,n)                                                  返回字符串s中從p開始長度爲n的後綴部分 1.gsub awk 'gsub(/^root/,"netseek") {print}' /etc/passwd             將與root開頭的root替換爲netseek

awk 'gsub(/0/,2) {print}' /etc/fstab awk '{print gsub(/0/,2) $0}' /etc/fstab                       2.index awk 'BEGIN {print index("root","o")}'  查詢字符串root中o出現的第一位置

awk -F: '$1=="root" {print index($1,"o")" "$1}' /etc/passwd

awk -F: '{print index($1,"o") $1}' /etc/passwd 3.length awk -F: '{print length($1)'} /etc/passwd

awk -F: '$1=="root" {print length($1)}' /etc/passwd 4.match (在ANCD中查找C的位置) awk 'BEGIN {print match("ANCD",/C/)}' 5.split 返回字符串數組元素個數

awk 'BEGIN {print split("123#456#789", myarray, "#")}' 6.sub   只能替換指定域的第一個0 awk 'sub(/0/,2) {print }' /etc/fstab 7.substr  按照起始位置及長度返回字符串的一部分

awk 'BEGIN {print substr("www.linuxtone.org",5,9)}'  //第5個字符開始,取9個字符。

awk 'BEGIN {print substr("www.linuxtone.org",5)}'  //第5個位置開始,取後面的所有. 字符串屏蔽序列 \b                  退格鍵 \f                  走紙換頁 \n                  新行 \r                  回車 \t                  tab \c                  任意其他特殊字符 \ddd                八進制 很簡單的例子

awk -F: '{print $1,"\b"$2,"\t"$3}' /etc/passwd 輸出函數printf(注意是printf不是print,兩者效果不同的)

printf函數擁有幾種不同的格式化輸出功能 printf修飾符 -                               左對齊 Width                        域的步長0表示0步長 .prec                          最大字符串長度,或小數點右邊的位數 awk printf格式 %c                              ASCII字符 %d                              整數 %e                              浮點數,科學計數法 %f                               浮點數 %g                              awk決定使用哪種浮點數轉換,e或者f %o                              八進制數 %s                              字符串 %x                              十六進制數 1.字符串轉換 echo "65" | awk '{printf"%c\n",$0}'

awk 'BEGIN {printf "%c\n",65}'

awk 'BEGIN {printf "%f\n",999}' 2.格式化輸出 awk -F: '{printf "%-15s %s\n",$1,$3}' /etc/passwd

awk -F: 'BEGIN {printf"USER\t\tUID\n"} {printf "%-15s %s\n",$1,$3}' /etc/passwd 3.向一行awk命令傳值 who | awk '{if ($1 == user) print $1 " you are connected to:" $2}' user=$LOGNAME 4.awk腳本文件 (在文件名字後面加後綴.awk方便區分) #!/bin/awk -f

BEGIN{

   FS=":"

       print "User\t\tUID"

       print"____________________________"

}

{printf "%-15s %s\n",$1,$3}

END{

      print "END"

}


發佈了19 篇原創文章 · 獲贊 24 · 訪問量 205萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章