awk是一個強大的文本分析工具,相對於grep的查找,sed的編輯,awk在其對數據分析並生成報告時,顯得尤其強大。簡單來講awk就是把文件逐行的讀入,以空格爲默認分隔符將每行切片,切開的部分再進行各類分析處理。html
awk有3個不一樣版本: awk、nawk和gawk,未做特別說明,通常指gawk,gawk 是 AWK 的 GNU 版本。python
awk其名稱得自於它的創始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首個字母。實際上 AWK 的確擁有本身的語言: AWK 程序設計語言 , 三位建立者已將它正式定義爲「樣式掃描和處理語言」。它容許您建立簡短的程序,這些程序讀取輸入文件、爲數據排序、處理數據、對輸入執行計算以及生成報表,還有無數其餘的功能。mysql
awk '{pattern + action}' {filenames}
儘管操做可能會很複雜,但語法老是這樣,其中 pattern 表示 AWK 在數據中查找的內容,而 action 是在找到匹配內容時所執行的一系列命令。花括號({})不須要在程序中始終出現,但它們用於根據特定的模式對一系列指令進行分組。 pattern就是要表示的正則表達式,用斜槓括起來。linux
awk語言的最基本功能是在文件或者字符串中基於指定規則瀏覽和抽取信息,awk抽取信息後,才能進行其餘文本操做。完整的awk腳本一般用來格式化文本文件中的信息。c++
一般,awk是以文件的一行爲處理單位的。awk每接收文件的一行,而後執行相應的命令,來處理文本。web
有三種方式調用awk正則表達式
1.命令行方式 awk [-F field-separator] 'commands' input-file(s) 其中,commands 是真正awk命令,[-F域分隔符]是可選的。 input-file(s) 是待處理的文件。 在awk中,文件的每一行中,由域分隔符分開的每一項稱爲一個域。一般,在不指名-F域分隔符的狀況下,默認的域分隔符是空格。 2.shell腳本方式 將全部的awk命令插入一個文件,並使awk程序可執行,而後awk命令解釋器做爲腳本的首行,一遍經過鍵入腳本名稱來調用。 至關於shell腳本首行的:#!/bin/sh 能夠換成:#!/bin/awk 3.將全部的awk命令插入一個單獨文件,而後調用: awk -f awk-script-file input-file(s) 其中,-f選項加載awk-script-file中的awk腳本,input-file(s)跟上面的是同樣的。
本章重點介紹命令行方式。sql
假設last -n 5的輸出以下shell
[root@www ~]# last -n 5 <==僅取出前五行 root pts/1 192.168.1.100 Tue Feb 10 11:21 still logged in root pts/1 192.168.1.100 Tue Feb 10 00:46 - 02:28 (01:41) root pts/1 192.168.1.100 Mon Feb 9 11:41 - 18:30 (06:48) dmtsai pts/1 192.168.1.100 Mon Feb 9 11:41 - 11:41 (00:00) root tty1 Fri Sep 5 14:09 - 14:10 (00:01)
若是隻是顯示最近登陸的5個賬號
#last -n 5 | awk '{print $1}'
root
root
root
dmtsai
root
awk工做流程是這樣的:讀入有'\n'換行符分割的一條記錄,而後將記錄按指定的域分隔符劃分域,填充域,$0則表示全部域,$1表示第一個域,$n表示第n個域。默認域分隔符是"空白鍵" 或 "[tab]鍵",因此$1表示登陸用戶,$3表示登陸用戶ip,以此類推。
若是隻是顯示/etc/passwd的帳戶
#cat /etc/passwd |awk -F ':' '{print $1}' root daemon bin sys
這種是awk+action的示例,每行都會執行action{print $1}。
-F指定域分隔符爲':'。
若是隻是顯示/etc/passwd的帳戶和帳戶對應的shell,而帳戶與shell之間以tab鍵分割
#cat /etc/passwd |awk -F ':' '{print $1"\t"$7}' root /bin/bash daemon /bin/sh bin /bin/sh sys /bin/sh
若是隻是顯示/etc/passwd的帳戶和帳戶對應的shell,而帳戶與shell之間以逗號分割,並且在全部行添加列名name,shell,在最後一行添加"blue,/bin/nosh"。
cat /etc/passwd |awk -F ':' 'BEGIN {print "name,shell"} {print $1","$7} END {print "blue,/bin/nosh"}' name,shell root,/bin/bash daemon,/bin/sh bin,/bin/sh sys,/bin/sh .... blue,/bin/nosh
awk工做流程是這樣的:先執行BEGING,而後讀取文件,讀入有/n換行符分割的一條記錄,而後將記錄按指定的域分隔符劃分域,填充域,$0則表示全部域,$1表示第一個域,$n表示第n個域,隨後開始執行模式所對應的動做action。接着開始讀入第二條記錄······直到全部的記錄都讀完,最後執行END操做。
搜索/etc/passwd有root關鍵字的全部行
#awk -F: '/root/' /etc/passwd root:x:0:0:root:/root:/bin/bash
這種是pattern的使用示例,匹配了pattern(這裏是root)的行纔會執行action(沒有指定action,默認輸出每行的內容)。
搜索支持正則,例如找root開頭的: awk -F: '/^root/' /etc/passwd
搜索/etc/passwd有root關鍵字的全部行,並顯示對應的shell
# awk -F: '/root/{print $7}' /etc/passwd /bin/bash
這裏指定了action{print $7}
awk有許多內置變量用來設置環境信息,這些變量能夠被改變,下面給出了最經常使用的一些變量。
ARGC 命令行參數個數 ARGV 命令行參數排列 ENVIRON 支持隊列中系統環境變量的使用 FILENAME awk瀏覽的文件名 FNR 瀏覽文件的記錄數 FS 設置輸入域分隔符,等價於命令行 -F選項 NF 瀏覽記錄的域的個數 NR 已讀的記錄數 OFS 輸出域分隔符 ORS 輸出記錄分隔符 RS 控制記錄分隔符
此外,$0變量是指整條記錄。$1表示當前行的第一個域,$2表示當前行的第二個域,......以此類推。
統計/etc/passwd:文件名,每行的行號,每行的列數,對應的完整行內容:
#awk -F ':' '{print "filename:" FILENAME ",linenumber:" NR ",columns:" NF ",linecontent:"$0}' /etc/passwd filename:/etc/passwd,linenumber:1,columns:7,linecontent:root:x:0:0:root:/root:/bin/bash filename:/etc/passwd,linenumber:2,columns:7,linecontent:daemon:x:1:1:daemon:/usr/sbin:/bin/sh filename:/etc/passwd,linenumber:3,columns:7,linecontent:bin:x:2:2:bin:/bin:/bin/sh filename:/etc/passwd,linenumber:4,columns:7,linecontent:sys:x:3:3:sys:/dev:/bin/sh
使用printf替代print,可讓代碼更加簡潔,易讀
awk -F ':' '{printf("filename:%10s,linenumber:%s,columns:%s,linecontent:%s\n",FILENAME,NR,NF,$0)}' /etc/passwd
awk中同時提供了print和printf兩種打印輸出的函數。
其中print函數的參數能夠是變量、數值或者字符串。字符串必須用雙引號引用,參數用逗號分隔。若是沒有逗號,參數就串聯在一塊兒而沒法區分。這裏,逗號的做用與輸出文件的分隔符的做用是同樣的,只是後者是空格而已。
printf函數,其用法和c語言中printf基本類似,能夠格式化字符串,輸出複雜時,printf更加好用,代碼更易懂。
變量和賦值
除了awk的內置變量,awk還能夠自定義變量。
下面統計/etc/passwd的帳戶人數
awk '{count++;print $0;} END{print "user count is ", count}' /etc/passwd root:x:0:0:root:/root:/bin/bash ...... user count is 40
count是自定義變量。以前的action{}裏都是隻有一個print,其實print只是一個語句,而action{}能夠有多個語句,以;號隔開。
這裏沒有初始化count,雖然默認是0,可是穩當的作法仍是初始化爲0:
awk 'BEGIN {count=0;print "[start]user count is ", count} {count=count+1;print $0;} END{print "[end]user count is ", count}' /etc/passwd [start]user count is 0 root:x:0:0:root:/root:/bin/bash ... [end]user count is 40
統計某個文件夾下的文件佔用的字節數
ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size}'
[end]size is 8657198
若是以M爲單位顯示:
ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size/1024/1024,"M"}'
[end]size is 8.25889 M
注意,統計不包括文件夾的子目錄。
條件語句
awk中的條件語句是從C語言中借鑑來的,見以下聲明方式:
if (expression) { statement; statement; ... ... } if (expression) { statement; } else { statement2; } if (expression) { statement1; } else if (expression1) { statement2; } else { statement3; }
統計某個文件夾下的文件佔用的字節數,過濾4096大小的文件(通常都是文件夾):
ls -l |awk 'BEGIN {size=0;print "[start]size is ", size} {if($5!=4096){size=size+$5;}} END{print "[end]size is ", size/1024/1024,"M"}'
[end]size is 8.22339 M
循環語句
awk中的循環語句一樣借鑑於C語言,支持while、do/while、for、break、continue,這些關鍵字的語義和C語言中的語義徹底相同。
數組
由於awk中數組的下標能夠是數字和字母,數組的下標一般被稱爲關鍵字(key)。值和關鍵字都存儲在內部的一張針對key/value應用hash的表格裏。因爲hash不是順序存儲,所以在顯示數組內容時會發現,它們並非按照你預料的順序顯示出來的。數組和變量同樣,都是在使用時自動建立的,awk也一樣會自動判斷其存儲的是數字仍是字符串。通常而言,awk中的數組用來從記錄中收集信息,能夠用於計算總和、統計單詞以及跟蹤模板被匹配的次數等等。
顯示/etc/passwd的帳戶
awk -F ':' 'BEGIN {count=0;} {name[count] = $1;count++;}; END{for (i = 0; i < NR; i++) print i, name[i]}' /etc/passwd 0 root 1 daemon 2 bin 3 sys 4 sync 5 games ......
這裏使用for循環遍歷數組
awk編程的內容極多,這裏只羅列簡單經常使用的用法,更多請參考 http://www.gnu.org/software/gawk/manual/gawk.html
awk很是的優秀,運行效率高,並且代碼簡單,對格式化的文本處理能力超強。基本上grep和sed能幹的活awk所有都能幹,並且幹得更好。
先來一個很爽的例子:
文件a,統計文件a的第一列中是浮點數的行的浮點數的平均值。用awk來實現只須要一句話就能夠搞定(固然,這個東東用python也能夠很輕鬆的實現,只是不管如何都得新建一個文件;別妄想用bash shell來作,那但是浮點數!!!)
$cat a
1.021 33
1#.ll 44
2.53 6
ss 7
awk 'BEGIN{total = 0;len = 0} {if($1~/^[0-9]+\.[0-9]*/){total += $1; len++}} END{print total/len}' a
niubility!
awk的語法:
awk [-F re] [parameter...] ['program'] [-f 'programfile'] [in_file_list]
awk裏面的BEGIN,END結構:
BEGIN和END中的語句分別在開始讀取文件(in_file)以前和讀取完文件以後發揮做用,能夠理解爲初始化和掃尾。
awk裏面的if..else; while ; do..while; for; break; continue; printf 語法都和C語言的語法一致;並且awk支持使用if (key in array)這樣的判斷語句(其中,array是數組,這一點和python的語法很是相像。);awk支持使用for (key in array)這樣的語法來遍歷數組(也是和python的語法很相像。)
我會用的參數的說明:
-F re:容許awk更改其字段分隔符
-v var=val 把val值賦值給var(變量通訊的好方法啊~~今天才知道這個選項,想一想以前寫的代碼,抓狂啊~~)若是有多個變量要賦值,那麼就寫多個-v,每一個變量賦值對應一個-v
e.g. 要打印文件a的第num行到num+num1行之間的行, awk -v num=$num -v num1=$num1 'NR==num,NR==num+num1{print}' a
-f progfile:容許awk調用並執行progfile程序文件,固然progfile必須是一個符合awk語法的程序文件
我會用的awk內置變量:
ARGC 命令行參數的個數
ARGV:命令行參數數組
ARGIND 當前被處理文件的ARGV標誌符
e.g 有兩個文件a 和b
awk '{if(ARGIND==1){print "處理a文件"} if(ARGIND==2){print "處理b文件"}}' a b
文件處理的順序是先掃描完a文件,再掃描b文件
NR 已經讀出的記錄數
FNR 當前文件的記錄數
上面的例子也能夠寫成這樣:
awk 'NR==FNR{print "處理文件a"} NR > FNR{print "處理文件b"}' a b
輸入文件a和b,因爲先掃描a,因此掃描a的時候必然有NR==FNR,而後掃描b的時候,FNR從1開始計數,而NR則接着a的行數繼續計數,因此NR > FNR
e.g 要顯示文件的第10行至第15行
awk 'NR==10,NR==15{print}' a
FS 輸入字段分隔符(缺省爲:space:),至關於-F選項
awk -F ':' '{print}' a 和 awk 'BEGIN{FS=":"}{print}' a 是同樣的
OFS輸出字段分隔符(缺省爲:space:)
awk -F ':' 'BEGIN{OFS=";"}{print $1,$2,$3}' b
若是cat b爲
1:2:3
4:5:6
那麼把OFS設置成";"後就會輸出
1;2;3
4;5;6
(小注釋:awk把分割後的第一、二、3個字段用$1,$2,$3...表示,$0表示整個記錄(通常就是一整行))
NF:當前記錄中的字段個數
awk -F ':' '{print NF}' b的輸出爲
3
3
代表b的每一行用分隔符":"分割後都3個字段
能夠用NF來控制輸出符合要求的字段數的行,這樣能夠處理掉一些異常的行
awk -F ':' '{if (NF == 3)print}' b
RS:輸入記錄分隔符,缺省爲"\n"
缺省狀況下,awk把一行看做一個記錄;若是設置了RS,那麼awk按照RS來分割記錄
例如,若是文件c,cat c爲
hello world; I want to go swimming tomorrow;hiahia
運行 awk 'BEGIN{ RS = ";" } {print}' c 的結果爲
hello world
I want to go swimming tomorrow
hiahia
合理的使用RS和FS可使得awk處理更多模式的文檔,例如能夠一次處理多行,例如文檔d cat d的輸出爲
1 2
3 4 5
6 7
8 9 10
11 12
hello
每一個記錄使用空行分割,每一個字段使用換行符分割,這樣的awk也很好寫
awk 'BEGIN{ FS = "\n"; RS = ""} {print NF}' d 輸出
2
3
1
ORS:輸出記錄分隔符,缺省爲換行符,控制每一個print語句後的輸出符號
awk 'BEGIN{ FS = "\n"; RS = ""; ORS = ";"} {print NF}' d 輸出
2;3;1
awk的數組:
awk的數組是一個很值得一說的東東。awk的數組從行爲上看的話更像關聯數組,或者說map、字典,或者說散列。awk的數組接受字符串下標,並接受速度很快的in查詢
文件e是由小寫的字母組成,cat e 輸出爲
a
b
z
...
若是要統計不一樣的字母出現的個數,那麼可使用數組來實現
awk '{arr[$0]++} END{ for (key in arr) print key, "-->",arr[key] }' e
使用for( key in arr)來遍歷數組的時候,輸出的次序是不可預測的,這一點跟python的字典遍歷是一致的。
在gawk中,可使用asort內置函數實現數組的排序,其餘的awk版本中尚未發現有相似的排序函數。一個折中的辦法是先awk完再用管道傳給sort來排序。sort使用-k選項能夠控制使用指定列排序。
awk的多維數組:
awk的多維數組在本質上是一維數組,更確切一點,awk在存儲上並不支持多維數組。awk提供了邏輯上模擬二維數組的訪問方式。例 如,array[2,4] = 1這樣的訪問是容許的。awk使用一個特殊的字符串SUBSEP (\034)做爲分割字段,在上面的例子中,關聯數組array存儲的鍵值其實是2\0344。
相似一維數組的成員測試,多維數組可使用 if ( (i,j) in array)這樣的語法,可是下標必須放置在圓括號中。
相似一維數組的循環訪問,多維數組使用 for ( item in array )這樣的語法遍歷數組。與一維數組不一樣的是,多維數組必須使用split()函數來訪問單獨的下標份量。split ( item, subscr, SUBSEP)
awk讀取shell中的變量
可使用-v選項實現功能
$b=1
$cat f
apple
$awk -v var=$b '{print var, $var}' f
1 apple
除了使用-v選項外,還可使用"'$variable'"的方式從shell往awk傳遞變量(注意:這裏是單引號)
$awk '{print $b, '$b'}' f
apple 1
至於有沒有辦法把awk中的變量傳給shell呢,這個問題我是這樣理解的。shell調用awk其實是fork一個子進程出來,而子進程是沒法向父進程傳遞變量的,除非用重定向(包括管道)
$a=$(awk '{print $b, '$b'}' f)
$echo $a
apple 1
getline
< SPAN>
< SPAN :>
|
< < var>
<>
<>省略時,表示置於$0)
<>
< SPAN>變量
<>省略時,表示置於$0)
$awk 'BEGIN{ "date" | getline d; close("date");print d}' f
Sun Nov 9 20:55:12 CST 2008
$awk 'BEGIN{getline name < "/dev/tty"} '
$awk 'BEGIN{while(getline < "/etc/passwd" > 0) { lc++ }; print lc }' f
只要getline的返回值大於0,即讀入一行,循環就會繼續。
getline若是如成功讀取,返回1,不然返回-1,若是遇到EOF,則返回0。getline在讀取的同時會設置NF,NR,FNR等內置變量
若是getline後沒有變量,則默認置於$0
$awk 'BEGIN{ while(("ls" | getline) > 0) print}' f
(以上3個例子來自UNIX Shells By Example Fourth Edition, Section 6.26.4)
輸出重定向
awk的輸出重定向相似於shell的重定向。重定向的目標文件名必須用雙引號引用起來。
$awk '$4 >=70 {print $1,$2 > "destfile" }' filename
$awk '$4 >=70 {print $1,$2 >> "destfile" }' filename
awk中調用shell命令:
1)使用管道
awk中的管道概念和shell的管道相似,都是使用"|"符號,在上面getline中{"date" | getline d;}就是使用了管道。若是在awk程序中打開了管道,必須先關閉該管道才能打開另外一個管道。也就是說一次只能打開一個管道。shell命令必須被雙引號引用起來。「若是打算再次在awk程序中使用某個文件或管道進行讀寫,則可能要先關閉程序,由於其中的管道會保持打開狀態直至腳本運行結束。注意,管道一旦被打開,就會保持打開狀態直至awk退出。所以END塊中的語句也會收到管道的影響。(能夠在END的第一行關閉管道)」
awk中使用管道有兩種語法,分別是:
awk output | shell input
shell output | awk input
對於awk output | shell input來講,shell接收awk的輸出,並進行處理。須要注意的是,awk的output是先緩存在pipe中,等輸出完畢後再調用shell命令 處理,shell命令只處理一次,並且處理的時機是「awk程序結束時,或者管道關閉時(須要顯式的關閉管道)」
$awk '/west/{count++} {printf "%s %s\t\t%-15s\n", $3,$4,$1 | "sort +1"} END{close "sort +1"; printf "The number of sales pers in the western"; printf "region is " count "." }' datafile
printf函數用於將輸出格式化併發送給管道。全部輸出集齊後,被一同發送給sort命令。必須用與打開時徹底相同的命令來關閉管道(sort +1),不然END塊中的語句將與前面的輸出一塊兒被排序。此處的sort命令只執行一次。
在shell output | awk input中awk的input只能是getline函數。shell執行的結果緩存於pipe中,再傳送給awk處理,若是有多行數據,awk的getline命令可能調用屢次。
$awk 'BEGIN{ while(("ls" | getline d) > 0) print d}' f
2)使用system命令
$awk 'BEGIN{system("echo abc")}'
須要注意的是system中應該使用shell命令的對應字符串。awk直接把system中的內容傳遞給shell,做爲shell的命令行。
3)system命令中使用awk的變量
空格是awk中的字符串鏈接符,若是system中須要使用awk中的變量可使用空格分隔,或者說除了awk的變量外其餘一概用""引用起來。
$awk 'BEGIN{a = 12; system("echo " a) }'
還有好多呀,之後再補充
awk的運算符
next等函數
更多的輸入輸出(輸出到多個文件,關閉文件,輸出到命令)
awk的內置函數:
gsub, index, length, match, printf, split, sprintf, substr, tolower, toupper, atan, cos, exp, int, log, rand, sin, sqrt, srand, system
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
. awk簡介
2. awk命令格式和選項
2.1. awk的語法有兩種形式
2.2. 命令選項
3. 模式和操做
3.1. 模式
3.2. 操做
4. awk的環境變量
5. awk運算符
6. 記錄和域
6.1. 記錄
6.2. 域
6.3. 域分隔符
7. gawk專用
8. POSIX字符集
9. 匹配操做符(~)
10. 比較表達式
11. 範圍模板
12. 一個驗證passwd
13. 幾個
14. awk編程
14.1. 變量
14.2. BEGIN模塊
14.3. END模塊
14.4. 重定向和管道
14.5. 條件
14.6.
14.7. 數組
14.8. awk的內建
1. awk簡介
awk是一種編程語言,用於在linux/unix下對文本和linux/unix下的一個強大編程工具。它在命令行中使用,但更可能是做爲腳原本使用。awk的處理文本和數據的方式是這樣的,它逐行掃描文件,從第一行到最後一行,尋找匹配的特定模式的行,並在這些行上進行你想要的操做。若是沒有指定處理動做,則把匹配的行顯示到標準輸出(屏幕),若是沒有指定模式,則全部被操做所指定的行都被處理。awk分別表明其做者姓氏的第一個字母。由於它的做者是三我的,分別是Alfred Aho、Brian Kernighan、Peter Weinberger。gawk是awk的GNU版本,它提供了Bell實驗室和GNU的一些擴展。下面介紹的awk是以GUN的gawk爲例的,在linuxawkawk進行介紹。
2. awk命令格式和選項
2.1. awk的語法有兩種形式
awk [options] 'script' var=value file(s)
awk [options] -f scriptfile var=value file(s)
2.2. 命令選項
-F fs or --field-separator fs
指定輸入文件折分隔符,fs是一個字符串或者是一個正則表達式,如-F:。
-v var=value or --asign var=value
賦值一個用戶定義變量。
-f scripfile or --file scriptfile
從腳本文件中讀取awk命令。
-mf nnn and -mr nnn
對nnn值設置內在限制,-mf選項限制分配給nnn的最大塊數目;-mr選項限制記錄的最大數目。這兩個功能是Bell實驗室版awk的擴展功能,在標準awk中不適用。
-W compact or --compat, -W traditional or --traditional
在兼容模式下運行awk。因此gawk的行爲和標準的awk徹底同樣,全部的awk擴展都被忽略。
-W copyleft or --copyleft, -W copyright or --copyright
打印簡短的版權信息。
-W help or --help, -W usage or --usage
打印所有awk選項和每一個選項的簡短說明。
-W lint or --lint
打印不能向傳統unix
打印關於不能向傳統unix平臺移植的結構的警告。
-W posix
打開兼容模式。但有如下限制,不識別:\x、函數關鍵字、func、換碼序列以及當fs是一個空格時,將新行做爲一個域分隔符;操做符**和**=不能代替^和^=;fflush無效。
-W re-interval or --re-inerval
容許間隔正則表達式的使用,參考(grep中的Posix字符類),如括號表達式[[:alpha:]]。
-W source program-text or --source program-text
使用program-text做爲源
打印bug報告信息的版本。
3. 模式和操做
awk腳本是由模式和操做組成的:
pattern {action} 如$ awk '/root/' test,或$ awk '$3 < 100' test。
二者是可選的,若是沒有模式,則action應用到所有記錄,若是沒有action,則輸出匹配所有記錄。默認狀況下,每個輸入行都是一條記錄,但用戶可經過RS變量指定不一樣的分隔符進行分隔。
3.1. 模式
模式能夠是如下任意一個:
/正則表達式/:使用通配符的擴展集。
關係表達式:能夠用下面運算符表中的關係運算符進行操做,能夠是字符串或數字的比較,如$2>%1選擇第二個字段比第一個字段長的行。
模式匹配表達式:用運算符~(匹配)和~!(不匹配)。
模式,模式:指定一個行的範圍。該語法不能包括BEGIN和END模式。
BEGIN:讓用戶指定在第一條輸入記錄被處理以前所發生的動做,一般可在這裏設置全局變量。
END:讓用戶在最後一條輸入記錄被讀取以後發生的動做。
3.2. 操做
操做由一人或多個命令、函數、表達式組成,之間由換行符或分號隔開,並位於大括號內。主要有四部份:
變量或數組賦值
輸出命令
內置函數
控制流命令
4. awk的環境變量
Table 1. 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)。
5. awk運算符
Table 2. 運算符
運算符 描述
= += -= *= /= %= ^= **= 賦值
?: C條件表達式
|| 邏輯或
&& 邏輯與
~ ~! 匹配正則表達式和不匹配正則表達式
< <= > >= != == 關係運算符
空格 鏈接
+ - 加,減
* / & 乘,除與求餘
+ - ! 一元加,減和邏輯非
^ *** 求冪
++ -- 增長或減小,做爲前綴或後綴
$ 字段引用
in 數組成員
6. 記錄和域
6.1. 記錄
awk把每個以換行符結束的行稱爲一個記錄。
記錄分隔符:默認的輸入和輸出的分隔符都是回車,保存在內建變量ORS和RS中。
$0變量:它指的是整條記錄。如$ awk '{print $0}' test將輸出test文件中的全部記錄。
變量NR:一個計數器,每處理完一條記錄,NR的值就增長1。如$ awk '{print NR,$0}' test將輸出test文件中全部記錄,並在記錄前顯示記錄號。
6.2. 域
記錄中每一個單詞稱作「域」,默認狀況下以空格或tab分隔。awk可跟蹤域的個數,並在內建變量NF中保存該值。如$awk '{print $1,$3}' test將打印test文件中第一和第三個以空格分開的列(域)。
6.3. 域分隔符
內建變量FS保存輸入域分隔符的值,默認是空格或tab。咱們能夠經過-F命令行選項修改FS的值。如$ awk -F: '{print $1,$5}' test將打印以冒號爲分隔符的第一,第五列的內容。
能夠同時使用多個域分隔符,這時應該把分隔符寫成放到方括號中,如$awk -F'[:\t]' '{print $1,$3}' test,表示以空格、冒號和tab做爲分隔符。
輸出域的分隔符默認是一個空格,保存在OFS中。如$ awk -F: '{print $1,$5}' test,$1和$5間的逗號就是OFS的值。
7. gawk專用正則表達式元字符
通常通用的元字符集就不講了,可參考個人Sed和Grepawk。
\Y
匹配一個單詞開頭或者末尾的空字符串。
\B
匹配單詞內的空字符串。
\<
匹配一個單詞的開頭的空字符串,錨定開始。
\>
匹配一個單詞的末尾的空字符串,錨定末尾。
\w
匹配一個字母數字組成的單詞。
\W
匹配一個非字母數字組成的單詞。
\‘
匹配字符串開頭的一個空字符串。
\'
匹配字符串末尾的一個空字符串。
8. POSIX字符集
可參考個人Grep學習筆記
9. 匹配操做符(~)
用來在記錄或者域內匹配正則表達式。如$ awk '$1 ~/^root/' test將顯示test文件第一列中以root開頭的行。
10. 比較表達式
conditional expression1 ? expression2: expression3,例如:$ awk '{max = {$1 > $3} ? $1: $3: print max}' test。若是第一個域大於第三個域,$1就賦值給max,不然$3就賦值給max。
$ awk '$1 + $2 < 100' test。若是第一和第二個域相加大於100,則打印這些行。
$ awk '$1 > 5 && $2 < 10' test,若是第一個域大於5,而且第二個域小於10,則打印這些行。
11. 範圍模板
範圍模板匹配從第一個模板的第一次出現到第二個模板的第一次出現之間全部行。若是有一個模板沒出現,則匹配到開頭或末尾。如$ awk '/root/,/
12. 一個驗證passwd文件有效性的例子
$ cat /etc/passwd | awk -F: '\
NF != 7{\
printf("line %d,does not have 7 fields:%s\n",NR,$0)}\
$1 !~ /[A-Za-z0-9]/{printf("line %d,non alpha and numeric user id:%d: %s\n,NR,$0)}\
$2 == "*" {printf("line %d, no password: %s\n",NR,$0)}'
cat把結果輸出給awk,awk把域之間的分隔符設爲冒號。
若是域的數量(NF)不等於7,就執行下面的程序。
printf打印字符串"line ?? does not have 7 fields",並顯示該條記錄。
若是第一個域沒有包含任何字母和數字,printf打印「no alpha and numeric user id" ,並顯示記錄數和記錄。
若是第二個域是一個星號,就打印字符串「no passwd」,緊跟着顯示記錄數和記錄自己。
13. 幾個實例
$ 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開頭的記錄爲止,或到文件末尾。
14. awk編程
14.1. 變量
在awk中,變量不須要定義就能夠直接使用,變量類型能夠是數字或字符串。
賦值格式:Variable = expression,如$ awk '$1 ~/test/{count = $2 + $3; print count}' test,上式的做用是,awk先掃描第一個域,一旦test匹配,就把第二個域的值加上第三個域的值,並把結果賦值給變量count,最後打印出來。
awk能夠在命令行中給變量賦值,而後將這個變量傳輸給awk腳本。如$ awk -F: -f awkscript month=4 year=2004 test,上式的month和year都是自定義變量,分別被賦值爲4和2004。在awk腳本中,這些變量使用起來就象是在腳本中創建的同樣。注意,若是參數前面出現test,那麼在BEGIN語句中的變量就不能被使用。
域變量也可被賦值和修改,如$ awk '{$2 = 100 + $1; print }' test,上式表示,若是第二個域不存在,awk將計算表達式100加$1的值,並將其賦值給$2,若是第二個域存在,則用表達式的值覆蓋$2原來的值。再例如:$ awk '$1 == "root"{$1 ="test";print}' test,若是第一個域的值是「root」,則把它賦值爲「test」,注意,字符串必定要用雙awk -F: '{IGNORECASE=1; $1 == "MARY"{print NR,$1,$2,$NF}'test,把IGNORECASE設爲1表明忽略大小寫,打印第一個域是mary的記錄數、第一個域、第二個域和最後一個域。
14.2. BEGIN模塊
BEGIN模塊後緊跟着動做塊,這個動做塊在awk處理任何輸入文件以前執行。因此它能夠在沒有任何輸入的狀況下進行測試。它一般用來改變內建變量的值,如OFS,RS和FS等,以及打印標題。如:$ awk 'BEGIN{FS=":"; OFS="\t"; ORS="\n\n"}{print $1,$2,$3} test。上式表示,在處理輸入文件之前,域分隔符(FS)被設爲冒號,輸出文件分隔符(OFS)被設置爲製表符,輸出記錄分隔符(ORS)被設置爲兩個換行符。$ awk 'BEGIN{print "TITLE TEST"}只打印標題。
14.3. END模塊
END不匹配任何的輸入文件,可是執行動做塊中的全部動做,它在整個輸入文件處理完成後被執行。如$ awk'END{print "The number of records is" NR}' test,上式將打印全部被處理的記錄數。
14.4. 重定向和管道
awk可以使用shell的重定向符進行重定向輸出,如:$ awk '$1 = 100 {print $1 > "output_file" }' test。上式表示若是第一個域的值等於100,則把它輸出到output_file中。也能夠用>>來重定向輸出,但不清空文件,只作追加操做。
輸出重定向需用到getline函數。getline從標準輸入、管道或者當前正在處理的文件以外的其餘輸入文件得到輸入。它負責從輸入得到下一行的內容,並給NF,NR和FNR等內建變量賦值。若是獲得一條記錄,getline函數返回1,若是到達文件的末尾就返回0,若是出現錯誤,例如打開文件失敗,就返回-1。如:
$ awk 'BEGIN{ "date" | getline d; print d}' test。執行linux的date命令,並經過管道輸出給getline,而後再把輸出賦值給自定義變量d,並打印它。
$ awk 'BEGIN{"date" | getline d; split(d,mon); print mon[2]}' test。執行shell的date命令,並經過管道輸出給getline,而後getline從管道中讀取並將輸入賦值給d,split函數把變量d轉化成數組mon,而後打印數組mon的第二個元素。
$ awk 'BEGIN{while( "ls" | getline) print}',命令ls的輸出傳遞給geline做爲輸入,循環使getline從ls的輸出中讀取一行,並把它打印到屏幕。這裏沒有輸入文件,由於BEGIN塊在打開輸入文件前執行,因此能夠忽略輸入文件。
$ awk 'BEGIN{printf "What is your name?"; getline name < "/dev/tty" } $1 ~name {print "Found" name on line ", NR "."} END{print "See you," name "."} test。在屏幕上打印」What is your name?",並等待用戶應答。當一行輸入完畢後,getline函數從終端接收該行輸入,並把它儲存在自定義變量name中。若是第一個域匹配變量name的值,print函數就被執行,END塊打印See you和name的值。
$ awk 'BEGIN{while (getline < "/etc/passwd" > 0) lc++; print lc}'。awk將逐行讀取文件/etc/passwd的內容,在到達文件末尾前,計數器lc一直增長,當到末尾時,打印lc的值。注意,若是文件不存在,getline返回-1,若是到達文件的末尾就返回0,若是讀到一行,就返回1,因此命令 while (getline < "/etc/passwd")在文件不存在的狀況下將陷入無限循環,由於返回-1表示邏輯真。
能夠在awk中打開一個管道,且同一時刻只能有一個管道存在。經過close()可關閉管道。如:$ awk '{print $1, $2 | "sort" }' test END {close("sort")}。awd把print語句的輸出經過管道做爲linux命令sort的輸入,END塊執行關閉管道操做。
system函數能夠在awk中執行linux的命令。如:$ awk 'BEGIN{system("clear")'。
fflush函數用以刷新輸出緩衝區,若是沒有參數,就刷新標準輸出的緩衝區,若是以空字符串爲參數,如fflush(""),則刷新全部文件和管道的輸出緩衝區。
14.5. 條件語句
awk中的條件語句是從C語言中借鑑過來的,可控制程序的流程。
14.5.1. if語句
格式:
{if (expression){
statement; statement; ...
}
}
$ awk '{if ($1 <$2) print $2 "too high"}' test。若是第一個域小於第二個域則打印。
$ awk '{if ($1 < $2) {count++; print "ok"}}' test.若是第一個域小於第二個域,則count加一,並打印ok。
14.5.2. if/else語句,用於雙重判斷。
格式:
{if (expression){
statement; statement; ...
}
else{
statement; statement; ...
}
}
$ 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。
14.5.3. if/else else if語句,用於多重判斷。
格式:
{if (expression){
statement; statement; ...
}
else if (expression){
statement; statement; ...
}
else if (expression){
statement; statement; ...
}
else {
statement; statement; ...
}
}
14.6. 循環
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表明成功,非零值表示出錯。
14.7. 數組
awk中的數組的下標能夠是數字和字母,稱爲關聯數組。
14.7.1. 下標與關聯數組
用變量做爲數組下標。如:$ awk {name[x++]=$2};END{for(i=0;i<NR;i++) print i,name}' test。數組name中的下標是一個自定義變量x,awk初始化x的值爲0,在每次使用後增長1。第二個域的值被賦給name數組的各個元素。在END模塊中,for循環被用於循環整個數組,從下標爲0的元素開始,打印那些存儲在數組中的值。由於下標是關健字,因此它不必定從0開始,能夠從任何值開始。
special for循環用於讀取關聯數組中的元素。格式以下:
{for (item in arrayname){
print arrayname[item]
}
}
$ awk '/^tom/{name[NR]=$1}; END{for(i in name){print name}}' test。打印有值的數組元素。打印的順序是隨機的。
用字符串做爲下標。如:count["test"]
用域值做爲數組的下標。一種新的for循環方式,for (index_value in array) statement。如:$ awk '{count[$1]++} END{for(name in count) print name,count[name]}' test。該語句將打印$1中字符串出現的次數。它首先以第一個域做數組count的下標,第一個域變化,索引就變化。
delete函數用於刪除數組元素。如:$ awk '{line[x++]=$1} END{for(x in line) delete(line[x])}' test。分配給數組line的是第一個域的值,全部記錄處理完成後,special for循環將刪除每個元素。
14.8. awk的內建函數
14.8.1. 字符串函數
sub函數匹配記錄中最大、最靠左邊的子字符串的正則表達式,並用替換字符串替換這些字符串。若是沒有指定目標字符串就默認使用整個記錄。替換隻發生在第一次匹配的時候。格式以下:
sub (regular expression, substitution string):
sub (regular expression, substitution string, target string)
實例:
$ awk '{ sub(/test/, "mytest"); print }' testfile
$ awk '{ sub(/test/, "mytest"); $1}; print }' testfile
第一個例子在整個記錄中匹配,替換隻發生在第一次匹配發生的時候。如要在整個文件中進行匹配須要用到gsub
第二個例子在整個記錄的第一個域中進行匹配,替換隻發生在第一次匹配發生的時候。
gsub函數做用如sub,但它在整個文檔中進行匹配。格式以下:
gsub (regular expression, substitution string)
gsub (regular expression, substitution string, target string)
實例:
$ awk '{ gsub(/test/, "mytest"); print }' testfile
$ awk '{ gsub(/test/, "mytest"), $1 }; print }' testfile
第一個例子在整個文檔中匹配test,匹配的都被替換成mytest。
第二個例子在整個文檔的第一個域中匹配,全部匹配的都被替換成mytest。
index函數返回子字符串第一次被匹配的位置,偏移量從位置1開始。格式以下:
index(string, substring)
實例:
$ awk '{ print index("test", "mytest") }' testfile
實例返回test在mytest的位置,結果應該是3。
length函數返回記錄的字符數。格式以下:
length( string )
length
實例:
$ awk '{ print length( "test" ) }'
$ awk '{ print length }' testfile
第一個實例返回test字符串的長度。
第二個實例返回testfile文件中第條記錄的字符數。
substr函數返回從位置1開始的子字符串,若是指定長度超過實際長度,就返回整個字符串。格式以下:
substr( string, starting position )
substr( string, starting position, length of string )
實例:
$ awk '{ print substr( "hello world", 7,11 ) }'
上例截取了world子字符串。
match函數返回在字符串中正則表達式位置的索引,若是找不到指定的正則表達式則返回0。match函數會設置內建變量RSTART爲字符串中子字符串的開始位置,RLENGTH爲到子字符串末尾的字符個數。substr可利於這些變量來截取字符串。函數格式以下:
match( string, regular expression )
實例:
$ awk '{start=match("this is a test",/[a-z]+$/); print start}'
$ awk '{start=match("this is a test",/[a-z]+$/); print start, RSTART, RLENGTH }'
第一個實例打印以連續小寫字符結尾的開始位置,這裏是11。
第二個實例還打印RSTART和RLENGTH變量,這裏是11(start),11(RSTART),4(RLENGTH)。
toupper和tolower函數可用於字符串大小間的轉換,該功能只在gawk中有效。格式以下:
toupper( string )
tolower( string )
實例:
$ awk '{ print toupper("test"), tolower("TEST") }'split函數可按給定的分隔符把字符串分割爲一個數組。若是分隔符沒提供,則按當前FS值進行分割。格式以下: split( string, array, field separator ) split( string, array )