find+xargs+grep+sed+awk系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.htmlhtml
1.awk入門:看視頻、找博客或者看《AWK程序設計語言》的第1-3章。正則表達式
2.awk進階:《awk程序設計語言》剩餘內容(剩餘的我也沒看過,哈哈),man awkshell
3.awk編程語言:用於掌控awk的語法和方方面面,推薦書籍《gnu awk: Effective AWK Programming》express
Awk自動地搜索輸入文件,並把每個輸入行切分紅字段。許多工做都是自動完成的,例如讀取每一個輸入行、字段分割、存儲管理、初始化等。在AWK中不需聲明變量數據類型,它內置字符串類型和數值類型。編程
通常來講,在CentOS上安裝的awk默認是gawk。它的調用格式爲:數組
awk [OPTIONS] -f program_file [--] filename_list
awk [OPTIONS] [--] program filename_list
program是awk程序的重中之重,稱爲awk的程序,它的格式爲PATTERN{ACTIONS}
。awk每讀入一行,都會先與PATTERN作匹配比較,當找到符合條件的數據就執行對應的ACTION。安全
其中PATTERN或ACTIONS兩者可省一。省略PATTERN時表示對全部行都執行ACTIONS,省略ACTIONS表示對符合條件的行執行默認的print動做。由於兩者可省一,因此用大括號{}將ACTIONS部分包圍起來,以區分PATTERN和ACTIONS。bash
一個簡單的例子,輸出/etc/passwd中用戶shell爲/bin/bash的用戶名,其中使用"-F"選項指定冒號做爲分隔符。編程語言
awk -F':' '$7 == "/bin/bash"{print "who use bash shell: ",$1}' /etc/passwd
其中位置變量$1,$2...爲該行的第幾個字段,"$0"表示整行。函數
若是要輸出多個字段,則字段之間使用逗號","分隔,例如{print $1,$5}
。但輸出時,仍默認以空格分隔各輸出字段。
若是action爲print $1 $5
,則結果會將"$1"和"$5"拼接在一塊兒,由於空格是awk中的拼接字符。例如變量賦值name = "abc" "bcd"
等價於name="abcbcd"
。其實不算是拼接符,而是由於awk會忽略任何不被引號包圍的空白。
awk使用print和printf輸出數據,不只能夠輸出到標準輸出中,還能夠重定向到文件中,使用管道傳遞給另外一個命令。
print $0
。 若是print或printf的參數列表中含有操做符,則須要使用括號包圍,不然容易產生歧義。如:
print($1, $3) > ($3 > 100 ? "bigpop" : "smallpop")
print $1, ($2 > $3)
執行系統命令的方式,能夠經過管道的方式,也能夠經過system()函數。注意包圍命令的引號加的位置。
awk 'BEGIN{name="ma long shuai";print (1,2,3,4) | "echo " name}'
awk 'BEGIN{while (("fdisk -l" | getline) >0){print $0}}' awk 'BEGIN{system("fdisk -l")}' awk 'BEGIN{name="ma long shuai";system("echo " name)}'
printf命令能夠輸出更格式化的數據。
printf(format, value1, value2, ... , valueN)
format是一個字符串,包含按字面輸出的純文本,還包含輸出格式,格式使用格式說明符"%"描述,後面跟着幾個字符,這些字符控制一個value的輸出格式。第一個"%"描述value1的輸出格式,第二個"%"描述value2的輸出格式,依次類推。所以,"%"的數量應該和被輸出的value數量同樣多。
例如:
{ printf("total pay for %s is $%.2f\n", $1, $2 * $3) }
{ printf("%-8s $%6.2f\n", $1, $2 * $3) }
第一個程序包含了兩個要格式化的value,分別是"$1"和"$2 * $3"。這兩個value的輸出格式分別被"%s"和"%.2f"描述,前者表示按字符串格式輸出"$1",後者表示按小數值格式輸出"$2 * $3",且小數位佔2位。因爲printf不自帶尾隨換行符,所以手動加一個換行符"\n"。
第二個程序,"%-8s"表示"$1"按字符串格式輸出,但短橫線"-"表示要左對齊輸出,"8"表示佔用8個字符寬度,不足之數在右邊空格補齊。"%6.2f"表示按小數格式輸出"$2 * $3",且小數位佔用2位,總字符數佔用6位。注意,小數點也佔用一個字符寬度。所以,一個可能的輸出值爲"123.20"。
格式說明符"%"後可跟如下幾個常見字符:
使用"-F"選項或設置內置變量"FS"能夠控制輸入行的字段分隔符,默認分隔符爲" "。可經過正則表達式指定分隔符,其實能夠認爲老是以正則方式指定分隔符。
如下是幾個示例和須要注意的空格分隔符:
-F " "
:默認會壓縮全部前導空白,包括製表符和空格。-F " :"
:當空格後跟一個冒號時做爲分隔符。會壓縮前導空格,但不會匹配製表符,更不會壓縮製表符。-F "[ ]'
:只表示一個空格,不壓縮任何空白。-F "|"
:指定豎線做爲分隔符。-F ",[ \t]*|[ \t]+"
:逗號後跟0或多個空白,或者只有1或多個空白時做爲分隔符。也就是說,當空格寫在最前面且不被中括號包圍限制的時候,總會忽略前導空格,但不必定能匹配製表符。
使用內置變量"RS"能夠控制輸入行的行分隔符,默認爲"\n",只有遇到行分隔符時才做爲"一行"記錄被讀取。
將其讀做行分隔符不標準,應該讀爲"記錄分隔符"。例如設置以製表符做爲記錄分隔符。
RS="\t"
記錄分隔符變量"RS"只識別第一個字符,若設置爲"\t\t",則第二個"\t"被忽略。可是控制輸出記錄分隔符的內置變量OFS則可識別多字符。
可經過設置FS="\n";RS=""
使得awk能處理多行記錄。但此時,本來的每行數據總體變成一個字段。
BEGIN和END是一個特殊的PATTERN,BEGIN引導的程序是在awk讀取第一個文件第一行前要執行的awk程序,END引導的程序是在awk處理完最後一個文件最後一行後要執行的awk程序。一般BEGIN用於輸出一個標題,或者初始化一些格式、變量等,END則用於最後的總結性輸出。
因此awk稍微完整一點的格式爲:
BGEIN{ACTIONS}PATTERN{ACTIONS}END{ACTIONS}
刨去BEGIN和END引導的兩個程序,中間處理輸入文件的程序PATTER{ACTIONS}稱爲"主輸入循環(main input loop)"。在進入主輸入循環以前,能夠不用提供輸入流,但進入主輸入循環後,必須提供輸入流。
例如,在開始處理文件前,設置輸出報表的頭部,在最後輸出總共輸出了多少行。其中print ""
表示輸出一個空行。
BEGIN{print "ID NAME GENDER GENDER";print ""}{print $0}END{print "total num: " NR}
awk的數組和shell的數組相似,都支持數值index的普通數組和字符串index的關聯數組,其實數值index仍然會轉換成字符串格式的index,因此awk的數組類型都是關聯數組。
數組格式:array_name[index]
數值賦值:array_name[1]=value1
引用數組:array_name[1]
須要注意的是,關聯數組的index必須使用雙引號包圍,例如array_name["ma"]
,若是寫成array_name[ma]
,則表示使用變量"ma"的值做爲index。若"ma"變量未定義,則這會定義一個新的數組array_name[""]
。
使用index in array_name
的方式能夠判斷數組array_name中是否有index下標對應的數組元素。若是有,它會返回1,不然返回0。因此判斷語句能夠以下:
if ( "ma" in array_name )
其實,判斷某個數組變量的值是否爲空也可判斷該數組元素是否存在,以下。但這有反作用,當該元素不存在時,會建立它。
if ( array_name["ma"] != "" )
for循環的一種變體:
for (i in array_name){
do something about array_name[i]
}
能夠用於遍歷數組,其中變量"i"是遍歷數組時的index,array_name是數組名。這是以遍歷index的方式遍歷數組。因爲index的順序隨機,因此遍歷時順序也是隨機的。固然,遍歷數組的方式有多種,以上只是for循環遍歷的一種方式。
使用delete語句能夠刪除數組中的元素或者刪除整個數組。以下:
delete array_name["ma"] # 刪除array_name中下標爲ma的元素
delete array_name # 刪除數組array_name
在ACTION中,可使用流程控制語句。包括但不限於:
if (expression) statements
if (expression) statements else statements
while (expression) statements
for (expression; expression; expression) statements
for (expression in array) statements
do statements while (expression)
還有幾個能影響循環的動做:
break:退出循環。
continue:退出當前循環,進入下一個循環
next:讀入下一行,並awk程序的頂端從頭開始。這個awk程序是PATTERN{action}這部分,不包括BEGIN{action}。
exit code:直接進入END,若本就在END中,則直接退出awk。若是END中的exit沒有定義code,則採用前一個exit的code。
if格式:
PATTERN {
if (test_cmd){
cmd1
cmd2
...
}
}
if-else格式爲:
PATTERN {
if (test_cmd){
cmd1
cmd2
......
}
else
cmd3
}
當if或else結構中的命令只有一個,則其內可省大括號,若是超過一個,則須要使用大括號。
若採用一行書寫格式,則以下:
PATTERN {if (test_cmd){cmd1;cmd2;...}else {cmd3;cmd4...}}
還有if-else if-else格式。
PATTERN {
if (test_cmd){cmd_list1}
else if {cmd_list2}
else if {cmd_list3}
else {cmd_list}
}
還支持多目操做符。
expression ? action1 : action2
其中"?"和":"還能夠繼續嵌套。
結構:
PATTERN {
cmd1
while (test_cmd)
cmd
}
當cmd有多個時,使用大括號包圍。
PATTERN {
cmd1
while (test_cmd){
cmd2
cmd3
....
}
}
一行書寫格式:
PATTERN{cmd1;while (test_cmd){cmd1;cmd2}}
和while循環相似,地位和shell中的until循環同樣。都是至少執行一次命令列表。
結構:
PATTERN {
do{
cmd1
cmd2
} while (test_cmd)
}
結構大體以下:
PATTERN {
for (i=1;i<=10;++i){
cmd1
cmd2
}
}
for後括號中包括:變量初始值,條件判斷和計數器增加表達式。
更完整的awk程序的語法格式有如下幾種:
BEGIN{actions} END{actions} expr{actions} /regexp/{actions}:可被regexp匹配的行才執行actions expr1,expr2{actions}:表示範圍,從知足expr1的行開始,到知足expr2的行結束
其中:
expr
是表達式。
< <= == != >= > ~ !~
。+ - * / % ^(取冪) **(取冪)
。其中**
非POSIX標準,不可移植。++ -- += -= *= /= %= ^= **=
。awk支持複合賦值,例如FS = OFS = "\t"
表示字段分隔符和輸出字段分隔符都被賦值爲製表符。/regexp/
爲正則匹配模式,表示該行能被regexp匹配則爲真。還有如下兩種匹配表達式,分別表示給定的字符串能(不能)匹配就爲真。
$4 == "Asia" && $3 > 500
,! (NR > 1 && NF > 3)
。awk中字符串和數值數據類型是自動轉換的。若是想要獲得一個字符串值,能夠value ""
進行轉換,同理,若是想要獲得一個數值,能夠value + 0
。
另外,正則表達式能夠不用包圍在兩個斜槓中。能夠將正則表達式賦值給一個變量,而後使用該變量取匹配數據。例如:
reg="^[0-9]+$"
$2 ~ reg
甚至直接使用雙引號替換斜槓也容許。但不建議使用,由於一個元字符可能須要多個反斜槓來保護,使得看上去極其晦澀。
普通變量:給變量賦值時,若是要賦值字符串,則該字符串應該使用雙引號包圍,特別是包含特殊字符時。賦值數值時無所謂。 字段變量:$1,$2,$3,...,$NF,還有"$0"表示整行內容。另外,能夠直接賦值一個新字段或修改字段值。但這都會影響"$0"。同理,修改"$0"也會影響各字段值。
內置變量:其實能夠分爲兩類,一類是awk內部自動修改的變量,如行數變量NR,一類是內部不會改動的系統變量,如輸入字段分隔符變量FS,徹底須要手動修改,這類通常都有默認值。
注意,像NR、FNR、RS等的對象是記錄(record),而非行。只有當RS="\n"時,讀取了一行才表示讀取了一條記錄。
awk有兩類內置函數:算術函數和字符串函數。還支持自定義函數。
算術函數:
0<=r<1
。 print srand()
輸出當前種子值。 所以,要生成一個範圍[1,n]的隨機數,使用int(n*rand() + 1)
,要四捨五入一個數值,使用int(x + 0.5)
。
隨機數的種子值相同時,rand的結果老是相同。以下兩次運行結果,兩次結果中,前兩個rand()值相同,後兩個rand()值不一樣,由於中間使用了srand()重設種子值。
awk 'BEGIN{print rand();print rand();srand();print rand();print rand();print srand()}'
0.237788
0.291066
0.109925
0.983692
1504560578
awk 'BEGIN{print rand();print rand();srand();print rand();print rand();print srand()}'
0.237788
0.291066
0.96322
0.670495
1504560604
字符串函數:建議下面的全部regexp都使用"//"包圍。
index(str1,str2)
:返回子串str2在字符串str1中第一次出現的位置。若是沒有指定str1,則返回0。length(str1)
:返回字符串str1的長度。若是未給定str1,則表示計算"$0"的長度。substr(str1,p)
:返回str1中從p位置開始的後綴字符串。substr(str1,p,n)
:返回str1中從p位置開始,長度爲n的子串。match(str1,regexp)
:若是regexp能匹配str1,則返回匹配起始位置。不然返回0。它會設置內置變量RSTART和RLENGTH的值。split(str1,array,sep)
:使用字段分隔符sep將str1分割到數組array中,並返回數組的元素個數。若是未指定sep則採用FS的值。所以該函數用於切分字段到數組中,下標從1開始。sprintf(fmt,expr)
:根據printf的格式fmt,返回格式化後的expr。sub(regexp,rep,str2)
:將str2中第一個被regexp匹配的字符串替換成rep,替換成功則返回1(表示替換了1次),不然返回0。注意是貪婪匹配。sub(regexp,rep)
:將"$0"中第一個被regexp匹配的字符串替換成rep,替換成功則返回1,不然返回0。注意是貪婪匹配。gsub(regexp,rep,str2)
:將str2中全部被regexp匹配的內容替換成rep,並返回替換的次數。gsub(regexp,rep)
:將"$0"中全部被regexp匹配的內容替換成rep,並返回替換的次數。toupper(str)
:將str轉換成大寫字母,並返回新串。tolower(str)
:將str轉換成小寫字母,並返回新串。關於替換函數sub和gsub,能夠在替換字符串rep中使用"&"符號表示反向引用,引用的是整個被匹配的部分。
awk 'BEGIN{ print index("banana","na") print length("banana") print match("banana","na.*") print toupper("banana") print substr("banana",3)}' 3 6 3 BANANA nana
awk 'BEGIN{str1="x&x";str2="banana"
print sub(/a.*n/,str1,str2)
print str2}'
1
bxananxa
awk 'BEGIN{ print match("banana",/a.*n/) print RSTART,RLENGTH}' 2 2 4
awk 'BEGIN{print sprintf("hello %i world %5s","123","abc")}' hello 123 world abc
awk 'BEGIN{ name="Ma long shuai" split(name,myname) for (i in myname){ print myname[i]} }' Ma long shuai
縱觀上述字符串函數,沒有一個函數能夠將匹配成功的字符串輸出出來。但藉助match()和RSTART、RLENGTH能夠實現。
例如,取出"Ma:long:shuai"中的"long"並輸出。
awk 'BEGIN{ name="Ma:long:shuai" if (match(name,/:[^:]*:/)){ print substr(name,RSTART+1,RLENGTH-2)}}' long
function name(parameter-list) {
statements
}
函數中的變量不影響函數外的變量,但可使用外部變量。參數列表使用逗號分隔,這些參數只在函數內部生效。
能夠在awk的引號內任意位置處定義函數(即便是BEGIN以前或END以後),且函數的調用位置能夠在函數的定義位置以前。但注意,函數必須不能定義在BEGIN或主輸入循環或END內部,不然自定義函數的大括號會和包圍action的大括號衝突而報錯。即以下(1)-(4)處位置可定義定義函數,在任意位置處調用函數。
awk '(1)BEGIN{ACTIONS}(2)PATTERN{ACTIONS}(3)END{ACTIONS}(4)'
在函數的statements中,可使用return expression
語句,表示函數的返回值。
例如,建立一個"向字符串指定位置處插入一個字符"的函數。
awk 'function insert(STRING, POS, INS) { before_tmp = substr(STRING, 1, POS) after_tmp = substr(STRING, POS + 1) return before_tmp INS after_tmp } BEGIN{print insert("banana",3,"x")}'
getline函數用於從文件、標準輸入或管道中讀取數據,並按狀況設置變量的值。getline能夠自動不斷的加載下一行。若是能讀取記錄,則getline的返回值爲1,遇到輸入流的尾部時,返回值爲0,不能讀取記錄(如文件沒有讀取權限、文件不存在)時,返回值爲「-1"。
其中:
getline
:會從主輸入文件中讀取記錄。會同時設置$0,NF,NR,FNR。getline var
:會從主輸入文件中讀取記錄,並將讀取的記錄賦值給變量var。會同時設置var,NR,FNR。getline <file
:從外部文件file中讀取記錄。同時會設置$0,NF。getline var <file
:從外部文件file中讀取記錄,並將讀取的記錄賦值給變量var。會同時設置var。cmd | getline
:從管道中讀取記錄。會同時設置$0,NF。cmd | getline var
:從管道中讀取記錄,並將讀取的記錄賦值給變量var。會同時設置var。也就是說:
仍然注意,從外部文件file中讀取記錄時,須要使用雙引號包圍文件名,不然被當成awk中的變量。
例如,執行Linux下的who命令並傳遞給getline讀取,每讀取一行記錄,變量n自增1。
while ("who" | getline)
n++
將Linux命令date的結果保存到awk的變量date中。
"date" | getline date
當寫成循環時,如:
while (getline <"file"){
cmd...
}
這是不安全的,由於當沒法讀取file時,返回值爲"-1",而while循環的判斷條件是0和非0,因此"-1"也會進入死循環。因此,安全的寫法爲:
while (getline <"file" >0){
cmd...
}
awk很重要且必備的能力是接受外界的變量,例如shell中的變量,shell中命令執行的結果,或者是在開始執行awk前應該初始化的變量。
例如,在shell中定義一個變量name,傳遞給awk使用。
awk -v awk_name="$name" 'BEGIN{print awk_name}'
Ma longshuai
有三種方式能夠向awk傳遞變量:
1.將待傳遞變量看成文件名被awk解析。awk識別後發現是賦值語句,就認爲其是變量傳遞。變量賦值語句必須定義awk program以後。此法定義的變量不可在BEGIN中使用,由於它是被當成文件解析的,只有在須要讀取主輸入文件的時候纔會被解析。
awk 'BEGIN{}PATTERN{print var1,var2,var3}' var1=value1 var2=value2 file1 var3=value3 var1=value4 file2
在上面的語句中,當awk執行完BEGIN程序後,準備讀取主輸入,因而開始解析program後的輸入文件。解析時發現,var1和var2都是賦值語句,因而當成變量處理,當讀取到file1時,發現只有一個參數,則看成輸入文件,因而開始處理該文件。在處理file1時,var1和var2都是有效的,但var3還未賦值,所以var3無效。當處理完file1後,繼續解析下一個主輸入文件,此時var3被賦值,並開始處理file2。在處理file2時,var一、var2和var3都是有效的,但var1被新值覆蓋。
此外,還能夠將shell命令的結果賦值給這些預約義變量。以下展現了幾種變量定義的方式:
name="Ma longshuai"
awk 'program' OFS=":" var1="$name" var2="`echo Ma longshuai2`" var3="Ma longshuai3" var4=Malongshuai4 filename
不只能夠定義普通變量,還能夠定義內置變量(如上OFS)。注意加引號的方式:爲了安全,應該對全部賦值語句的value部分加上雙引號,除非所賦的值不包含特殊字符。因此,若是上面的var1賦值語句寫成var1=$name
,將被awk解析成var1=Ma longshuai
,因而var1的值爲Ma,主輸入文件爲longshuai。
2.使用"-v"選項傳遞。變量賦值語句必須定義在awk program以前。這種方法定義的變量能夠在BEGIN程序中使用。
除了定義在program以前,定義方式同上。每定義一個變量,都須要使用一個"-v"選項。如:
name="Ma longshuai"
awk -v OFS=":" -v var1="$name" -v var2="`echo Ma longshuai2`" -v var3="Ma longshuai3" 'program' filename
3.經過參數數組ARGV的方式。
ARGV是內置的數組變量。awk內部會將命令行切分,並按規則將各參數存放到ARGV數組中,數組下標從0開始,這是awk中惟一下標從0開始的數組。在存放到ARGV時,全部的選項和program會被忽略。
每存儲一個數組變量,特殊變量ARGC的值增長1。所以ARGC的值表明的是參數的個數。因此,數組變量從ARGV[0]到ARGV[ARGC-1]。
可以使用相似下面的循環來遍歷ARGV數組。
awk -F "\t" -v var1="value1" 'BEGIN{ for(i=0;i<ARGC;++i){ print "ARGV[" i "]: " ARGV[i] } print "ARGC: " ARGC }' "a" "b" "v=1" file
ARGV[0]: awk
ARGV[1]: a
ARGV[2]: b
ARGV[3]: v=1
ARGV[4]: file
ARGC: 5
注意,ARGV[0]存儲的是awk命令,"-F"和"-v"選項都沒有存儲到ARGV中。
ARGC和ARGV數組變量的值均可以手動修改。命令行分割存儲完成以後,開始處理BEGIN,再處理主循環輸入。所以,在BEGIN中修改ARGV中輸入文件對應的值,能夠改變awk所讀取的輸入文件,若將其設置爲空,則該數組變量直接被跳過,也就再也不讀取該輸入文件。
須要注意的是,當增長ARGV元素時,必須同時遞增ARGC的值,由於awk是根據AGRC來讀取ARGV的。同理,只增長ARGC的值,將致使新建ARGV數組元素,且這些新元素的值爲空。也所以,若是減少ARGC的值,將致使沒法訪問超出ARGC-1邊界的ARGV元素。