詳解 awk 工具的簡單使用方法


來源:jarly 

https://my.oschina.net/jarly/blog/898144

 

【轉注】:awk 是一個強大的文本分析工具,本教材只是簡單的入門。結合Linux其它文件操作,shell腳本等強大的會爆表,經常接觸文件分析的同學建議深入學習。

 

當你第一次拿起雙手在電腦上使用 awk 命令處理一個或者多個文件的時候,它會依次讀取文件的每一行內容, 然后對其進行處理,awk 命令默認從 stdio 標准輸入獲取文件內容, awk 使用一對單引號來表示 一些可執行的腳本代碼,在可執行腳本代碼里面,使用一對花括號來表示一段可執行代碼塊,可以同時存在多個代碼塊。 awk 的每個花括號內同時又可以有多個指令,每一個指令用分號分隔,awk 其實就是一個腳本編程語言。說了這么多,你肯定還是一臉的懵逼。你猜對了,上面這些都是廢話。先別急,客官請往下看……

 

awk 命令的基本格式

 

awk [options] 'program' file

 

options 這個表示一些可選的參數選項,反正就是你愛用不用,不用可以拉到。。。 program 這個表示 awk 的可執行腳本代碼,這個是必須要有的。 file 這個表示 awk 需要處理的文件,注意是純文本文件,不是你的 mp3,也不是 mp4 啥的。。

 

先來一個 awk 的使用例子熱熱身

 

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

 

awk 命令的可執行腳本代碼使用單引號括起來,緊接着里面是一對花括號,記住是 “花括號” 不是 “花姑娘”,然后花括號里面就是一些可執行的腳本代碼段,當 awk 每讀取一行之后,它會依次執行雙引號里面的每個腳本代碼段,在上面這個例子中, $0 表示當前行。當你執行了上面的命令之后,它會依次將 /etc/passwd 文件的每一行內容打印輸出,你一定在想:這有個毛用,用 cat 命令也能搞定。沒錯!上面這個命令沒個毛用,請往下看。

 

awk 自定義分隔符

 

awk 默認的分割符為空格和制表符,我們可以使用 -F 參數來指定分隔符

 

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

root

bin

daemon

adm

lp

sync

shutdown

halt

mail

operator

games

ftp

nobody

 

上面的命令將 /etc/passwd 文件中的每一行用冒號 : 分割成多個字段,然后用 print 將第 1 列字段的內容打印輸出

 

如何在 awk 中同時指定多個分隔符

 

比如現在有這樣一個文件 some.log 文件內容如下

 

Grape(100g)1980

raisins(500g)1990

plum(240g)1997

apricot(180g)2005

nectarine(200g)2008

 

現在我們想將上面的 some.log 文件中按照 “水果名稱(重量)年份” 來進行分割

 

$ awk -F '[()]' '{print $1, $2, $3}' some.log

Grape 100g 1980

raisins 500g 1990

plum 240g 1997

apricot 180g 2005

nectarine 200g 2008

 

在 -F 參數中使用一對方括號來指定多個分隔符,awk 處理 some.log 文件時就會使用 “(” 和 “)” 來對文件的每一行進行分割。

 

awk 內置變量的使用

 

$0 這個表示文本處理時的當前行

 

$1 表示文本行被分隔后的第 1 個字段列

 

$2 表示文本行被分割后的第 2 個字段列

 

$3 表示文本行被分割后的第 3 個字段列

 

$n 表示文本行被分割后的第 n 個字段列

 

NR 表示文件中的行號,表示當前是第幾行

 

NF 表示文件中的當前行列的個數,類似於 mysql 數據表里面每一條記錄有多少個字段

 

FS 表示 awk 的輸入分隔符,默認分隔符為空格和制表符,你可以對其進行自定義設置

 

OFS 表示 awk 的輸出分隔符,默認為空格,你也可以對其進行自定義設置

 

FILENAME 表示當前文件的文件名稱,如果同時處理多個文件,它也表示當前文件名稱

 

比如我們有這么一個文本文件 fruit.txt 內容如下,我將用它來向你演示如何使用 awk 命令工具,順便活躍一下此時尷尬的氣氛。。

 

peach    100   Mar  1997   China

Lemon    150   Jan  1986   America

Pear     240   Mar  1990   Janpan

avocado  120   Feb  2008   china

 

我們來瞧一瞧下面這些簡單到爆炸的例子,這個表示打印輸出文件的每一整行的內容

 

$ awk '{print $0}' fruit.txt

peach    100   Mar  1997   China

Lemon    150   Jan  1986   America

Pear     240   Mar  1990   Janpan

avocado  120   Feb  2008   china

 

下面這個表示打印輸出文件的每一行的第 1 列內容

 

$ awk '{print $1}' fruit.txt

peach

Lemon

Pear

avocado

 

下面面這個表示打印輸出文件的每一行的第 1 列、第 2 列和第 3 列內容

 

$ awk '{print $1, $2, $3}' fruit.txt

peach 100 Mar

Lemon 150 Jan

Pear 240 Mar

avocado 120 Feb

 

其中加入的逗號表示插入輸出分隔符,也就是默認的空格

 

文件的每一行的每一列的內容除了可以用 print 命令打印輸出以外,還可以對其進行賦值

 

$ awk '{$2 = "***"; print $0}' fruit.txt

peach *** Mar 1997 China

Lemon *** Jan 1986 America

Pear *** Mar 1990 Janpan

avocado *** Feb 2008 china

 

上面的例子就是表示通過對 $2 變量進行重新賦值,來隱藏每一行的第 2 列內容,並且用星號 * 來代替其輸出

 

在參數列表中加入一些字符串或者轉義字符之類的東東

 

$ awk '{print $1 "\t" $2 "\t" $3}' fruit.txt

peach   100     Mar

Lemon   150     Jan

Pear    240     Mar

avocado 120     Feb

 

像上面這樣,你可以在 print的參數列表中加入一些字符串或者轉義字符之類的東東,讓輸出的內容格式更漂亮,但一定要記住要使用雙引號。

 

awk 內置 NR 變量表示每一行的行號

 

$ awk '{print NR "\t" $0}' fruit.txt

1   peach    100   Mar  1997   China

2   Lemon    150   Jan  1986   America

3   Pear     240   Mar  1990   Janpan

4   avocado  120   Feb  2008   china

 

awk 內置 NF 變量表示每一行的列數

 

$ awk '{print NF "\t" $0}' fruit.txt

5   peach    100   Mar  1997   China

5   Lemon    150   Jan  1986   America

5   Pear     240   Mar  1990   Janpan

5   avocado  120   Feb  2008   china

 

awk 中 $NF 變量的使用

 

$ awk '{print $NF}' fruit.txt

China

America

Janpan

china

 

上面這個 $NF 就表示每一行的最后一列,因為 NF 表示一行的總列數,在這個文件里表示有 5 列,然后在其前面加上 $ 符號,就變成了 $5 ,表示第 5 列

 

$ awk '{print $(NF - 1)}' fruit.txt

1997

1986

1990

2008

 

上面 $(NF-1) 表示倒數第 2 列, $(NF-2) 表示倒數第 3 列,依次類推。

 

yahoo   100 4500

google  150 7500

apple   180 8000

twitter 120 5000

 

我們用 fruit.txt 和 company.txt 兩個文件來向你演示 awk 同時處理多個文件的時候有什么效果

 

$ awk '{print FILENAME "\t" $0}' fruit.txt company.txt

fruit.txt       peach    100   Mar  1997   China

fruit.txt       Lemon    150   Jan  1986   America

fruit.txt       Pear     240   Mar  1990   Janpan

fruit.txt       avocado  120   Feb  2008   china

company.txt     yahoo   100 4500

company.txt     google  150 7500

company.txt     apple   180 8000

company.txt     twitter 120 5000

 

當你使用 awk 同時處理多個文件的時候,它會將多個文件合並處理,變量 FILENAME 就表示當前文本行所在的文件名稱。

 

看到這里是不是感覺 awk 命令的使用方法真的是簡單到爆炸,現在不要太高興,請舉起你的雙手跟我一起搖擺。。。哦,不對!請拿起你的雙手在電腦上試一試上面這些例子。 你會知道我沒有騙你,因為講了這么多,傻子都會了。。。—_—

 

BEGIN 關鍵字的使用

 

在腳本代碼段前面使用 BEGIN 關鍵字時,它會在開始讀取一個文件之前,運行一次 BEGIN 關鍵字后面的腳本代碼段, BEGIN 后面的腳本代碼段只會執行一次,執行完之后 awk 程序就會退出

 

$ awk 'BEGIN {print "Start read file"}' /etc/passwd

Start read file

 

awk 腳本中可以用多個花括號來執行多個腳本代碼,就像下面這樣

 

$ awk 'BEGIN {print "Start read file"} {print $0}' /etc/passwd

Start read file

root:x:0:0:root:/root:/bin/bash

bin:x:1:1:bin:/bin:/sbin/nologin

daemon:x:2:2:daemon:/sbin:/sbin/nologin

adm:x:3:4:adm:/var/adm:/sbin/nologin

lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

sync:x:5:0:sync:/sbin:/bin/sync

shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

halt:x:7:0:halt:/sbin:/sbin/halt

mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

operator:x:11:0:operator:/root:/sbin/nologin

games:x:12:100:games:/usr/games:/sbin/nologin

ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

nobody:x:99:99:Nobody:/:/sbin/nologin

 

END 關鍵字使用方法

 

awk 的 END 指令和 BEGIN 恰好相反,在 awk 讀取並且處理完文件的所有內容行之后,才會執行 END 后面的腳本代碼段

 

$ awk 'END {print "End file"}' /etc/passwd

End file

 

一定要多動手在電腦上敲一敲這些命令,對身體好。腦子是個好東西,要多用。。

 

$ awk 'BEGIN {print "Start read file"} {print $0} END {print "End file"}' /etc/passwd

Start read file

root:x:0:0:root:/root:/bin/bash

bin:x:1:1:bin:/bin:/sbin/nologin

daemon:x:2:2:daemon:/sbin:/sbin/nologin

adm:x:3:4:adm:/var/adm:/sbin/nologin

lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

sync:x:5:0:sync:/sbin:/bin/sync

shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

halt:x:7:0:halt:/sbin:/sbin/halt

mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

operator:x:11:0:operator:/root:/sbin/nologin

games:x:12:100:games:/usr/games:/sbin/nologin

ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

nobody:x:99:99:Nobody:/:/sbin/nologin

End file

 

在 awk 中使用變量

 

可以在 awk 腳本中聲明和使用變量

 

$ awk '{msg="hello world"; print msg}' /etc/passwd

hello world

hello world

hello world

hello world

hello world

awk 聲明的變量可以在任何多個花括號腳本中使用

 

$ awk 'BEGIN {msg="hello world"} {print msg}' /etc/passwd

hello world

hello world

hello world

hello world

hello world

 

在 awk 中使用數學運算

 

在 awk 中,像其他編程語言一樣,它也支持一些基本的數學運算操作

 

$ awk '{a = 12; b = 24; print a + b}' company.txt
36
36
36
36

 

上面這段腳本表示,先聲明兩個變量 a = 12 和 b = 24,然后用 print 打印出 a 加上 b 的結果。

 

看到上面的輸出結果,你很可能又是一臉的懵逼,為什么會重復輸出 4 次同樣的計算結果。所以說小時不學好,長大做IT。 知識這東西真到了要用的時候,能亮瞎別人的雙眼,好了,不廢話。請記住 awk 是針對文件的每一行來執行一次單引號 里面的腳本代碼,每讀取到一行就會執行一次,文件里面有多少行就會執行多少次,但 BEGIN 和 END 關鍵字后面的 腳本代碼除外,如果被處理的文件中什么都沒有,那 awk 就一次都不會執行。。。

 

awk 還支持其他的數學運算符

 

  • + 加法運算符

  • - 減法運算符

  • * 乘法運算符

  • / 除法運算符

  • % 取余運算符

 

在 awk 中使用條件判斷

 

比如有一個文件 company.txt 內容如下

 

yahoo   100 4500

google  150 7500

apple   180 8000

twitter 120 5000

 

我們要判斷文件的第 3 列數據,也就是平均工資小於 5500 的公司,然后將其打印輸出

 

$ awk '$3 < 5500 {print $0}' company.txt

yahoo   100 4500

twitter 120 5000

 

上面的命令結果就是平均工資小於 5500 的公司名單, $3 < 5500 表示當第 3 列字段的內容小於 5500 的時候才會執行后面的 {print $0} 代碼塊

 

$ awk '$1 == "yahoo" {print $0}' company.txt

yahoo   100 4500

 

awk 還有一些其他的條件操作符如下

 

  • < 小於

  • <= 小於或等於

  • == 等於

  • != 不等於

  • > 大於

  • >= 大於或等於

  • ~ 匹配正則表達式

  • !~ 不匹配正則表達式

 

使用 if 指令判斷來實現上面同樣的效果

 

$ awk '{if ($3 < 5500) print $0}' company.txt

yahoo   100 4500

twitter 120 5000

 

上面表示如果第 3 列字段小於 5500 的時候就會執行后面的 print $0,很像 C 語言和 PHP 的語法對不對。 想到這里有一句話不知當講不當講,那就是 PHP 是世界上最好的語言。。。 我可能喝多了, 但是突然想起來我好像從來不喝酒。。。—_—

 

在 awk 中使用正則表達式

 

在 awk 中支持正則表達式的使用,如果你還對正則表達式不是很了解,請先停下來,上 google 去搜一下。

 

比如現在我們有這么一個文件 poetry.txt 里面都是我寫的詩,不要問我為什么那么的有才華。內容如下:

 

This above all: to thine self be true

There is nothing either good or bad, but thinking makes it so

There’s a special providence in the fall of a sparrow

No matter how dark long, may eventually in the day arrival

 

使用正則表達式匹配字符串 “There” ,將包含這個字符串的行打印並輸出

 

$ awk '/There/{print $0}' poetry.txt

There is nothing either good or bad, but thinking makes it so

There’s a special providence in the fall of a sparrow

 

使用正則表達式配一個包含字母 t 和字母 e ,並且 t 和 e 中間只能有任意單個字符的行

 

$ awk '/t.e/{print $0}' poetry.txt

There is nothing either good or bad, but thinking makes it so

There’s a special providence in the fall of a sparrow

No matter how dark long, may eventually in the day arrival

 

如果只想匹配單純的字符串 “t.e”, 那正則表達式就是這樣的 /t\.e/ ,用反斜杠來轉義 . 符號 因為 . 在正則表達式里面表示任意單個字符。

 

使用正則表達式來匹配所有以 “The” 字符串開頭的行

 

$ awk '/^The/{print $0}' poetry.txt

There is nothing either good or bad, but thinking makes it so

There’s a special providence in the fall of a sparrow

 

在正則表達式中 ^ 表示以某某字符或者字符串開頭。

 

使用正則表達式來匹配所有以 “true” 字符串結尾的行

 

$ awk '/true$/{print $0}' poetry.txt

This above all: to thine self be true

 

在正則表達式中 $ 表示以某某字符或者字符串結尾。

 

又一個正則表達式例子如下

 

$ awk '/m[a]t/{print $0}' poetry.txt

No matter how dark long, may eventually in the day arrival

 

上面這個正則表達式 /m[a]t/ 表示匹配包含字符 m ,然后接着后面還要包含中間方括號中表示的單個字符 a ,最后還要包含字符 t 的行,輸出結果中只有單詞 “matter” 符合這個正則表達式的匹配。因為正則表達式 [a] 方括號中表示匹配里面的任意單個字符。

 

繼續上面的一個新例子如下

 

$ awk '/^Th[ie]/{print $0}' poetry.txt

This above all: to thine self be true

There is nothing either good or bad, but thinking makes it so

There’s a special providence in the fall of a sparrow

 

這個例子中的正則表達式 /^Th[ie]/表示匹配以字符串 “Thi” 或者 “The” 開頭的行,正則表達式方括號中表示匹配其中的任意單個字符。

 

再繼續上面的新的用法

 

$ awk '/s[a-z]/{print $0}' poetry.txt

This above all: to thine self be true

There is nothing either good or bad, but thinking makes it so

There’s a special providence in the fall of a sparrow

 

正則表達式 /s[a-z]/ 表示匹配包含字符 s 然后后面跟着任意 a 到 z 之間的單個字符的字符串,比如 “se”, “so”, “sp” 等等。

 

正則表達式 [] 方括號中還有一些其他用法比如下面這些

 

[a-zA-Z]  表示匹配小寫的 a 到 z 之間的單個字符,或者大寫的 A 到 Z 之間的單個字符

[^a-z]    符號 `^` 在方括號里面表示取反,也就是非的意思,表示匹配任何非 a 到 z 之間的單個字符

 

正則表達式中的星號 * 和加號 + 的使用方法

 

$ awk '/go*d/{print $0}' poetry.txt

There is nothing either good or bad, but thinking makes it so

 

上面這個表示匹配包含字符串 “god”,並且中間的字母 “o” 可以出現 0 次或者多次,比如單詞 “good” 就符合這個要求。 正則表達式中的 + 和星號原理差不多,只是加號表示任意 1 個或者 1 個以上,也就是必須至少要出現一次。

 

正則表達式問號 ? 的使用方法

 

$ awk '/ba?d/{print $0}' poetry.txt

There is nothing either good or bad, but thinking makes it so

 

正則表達式中的問號 ? 表示它前面的字符只能出現 0 次 或者 1 次,也就是可以不出現,也可以出現,但如果有出現也只能出現 1 次。

 

正則表達式中的 {} 花括號用法

 

$ awk '/go{2}d/{print $0}' poetry.txt

There is nothing either good or bad, but thinking makes it so

 

花括號 {} 表示規定它前面的字符必須出現的次數,像上面這個 /go{2}d/ 就表示只匹配字符串 “good”,也就是中間的字母 “o” 必須要出現 2 次。

 

正則表達式中的花括號還有一些其他的用法如下

 

/go{2,3}d/    表示字母 "o" 只能可以出現 2 次或者 3 次

/go{2,10}d/   表示字母 "o" 只能可以出現 2次,3次,4次,5次,6次 ... 一直到 10 次

/go{2,}d/     表示字母 "o" 必須至少出現 2 次或着 2 次以上

 

正則表達式中的圓括號的用法

 

$ awk '/th(in){1}king/{print $0}' poetry.txt

There is nothing either good or bad, but thinking makes it so

 

正則表達式中的圓括號表示將多個字符當成一個完整的對象來看待。比如 /th(in){1}king/ 就表示其中字符串 “in” 必須出現 1 次。而如果不加圓括號就變成了 /thin{1}king/ 這個就表示其中字符 “n” 必須出現 1 次。

 

看到這里,如果你對 poetry.txt 件中寫的詩比較熟悉,你就會發現。。。我去!這詩根本就不是我寫的。所以論多讀書是多么的重要。我有幸借用莎士比亞的詩來向你講解如何在 awk 中使用正則表達式。現在該想想晚上吃什么,晚上去吃火鍋。。。—_—

 

使用 awk 的一些總結

 

因為 awk 算起來也是一種編程語言,它的功能遠遠不止我們上面講的這些,awk 還有一些其他比較復雜的功能。但一般我們不建議將 awk 用的太過於復雜。通常面對一些比較復雜的場景我們還是要使用其他的一些工具,比如 shell 腳本,Lua 等等……


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM