linux命令awk的詳解

簡介

awk是一個強大的文本分析工具,相對於grep的查找,sed的編輯,awk在其對數據分析並生成報告時,顯得尤其強大。簡單來講awk就是把文件逐行的讀入,以空格爲默認分隔符將每行切片,切開的部分再進行各類分析處理。html

awk有3個不一樣版本: awk、nawk和gawk,未做特別說明,通常指gawk,gawk 是 AWK 的 GNU 版本。mysql

awk其名稱得自於它的創始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首個字母。實際上 AWK 的確擁有本身的語言: AWK 程序設計語言 , 三位建立者已將它正式定義爲「樣式掃描和處理語言」。它容許您建立簡短的程序,這些程序讀取輸入文件、爲數據排序、處理數據、對輸入執行計算以及生成報表,還有無數其餘的功能。正則表達式

 

使用方法

awk '{pattern + action}' {filenames}

儘管操做可能會很複雜,但語法老是這樣,其中 pattern 表示 AWK 在數據中查找的內容,而 action 是在找到匹配內容時所執行的一系列命令。花括號({})不須要在程序中始終出現,但它們用於根據特定的模式對一系列指令進行分組。 pattern就是要表示的正則表達式,用斜槓括起來。sql

awk語言的最基本功能是在文件或者字符串中基於指定規則瀏覽和抽取信息,awk抽取信息後,才能進行其餘文本操做。完整的awk腳本一般用來格式化文本文件中的信息。shell

一般,awk是以文件的一行爲處理單位的。awk每接收文件的一行,而後執行相應的命令,來處理文本。express

 

調用awk

有三種方式調用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)跟上面的是同樣的。
複製代碼

 本章重點介紹命令行方式。數組

 

入門實例

假設last -n 5的輸出以下bash

[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內置變量

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

 

print和printf

awk中同時提供了print和printf兩種打印輸出的函數。

其中print函數的參數能夠是變量、數值或者字符串。字符串必須用雙引號引用,參數用逗號分隔。若是沒有逗號,參數就串聯在一塊兒而沒法區分。這裏,逗號的做用與輸出文件的分隔符的做用是同樣的,只是後者是空格而已。

printf函數,其用法和c語言中printf基本類似,能夠格式化字符串,輸出複雜時,printf更加好用,代碼更易懂。

 

 awk編程

 變量和賦值

除了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編程的內容極多,這裏只羅列簡單經常使用的用法

 

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

awk是行處理器: 相比較屏幕處理的優勢,在處理龐大文件時不會出現內存溢出或是處理緩慢的問題,一般用來格式化文本信息
awk處理過程: 依次對每一行進行處理,而後輸出
awk命令形式:
awk [-F|-f|-v] ‘BEGIN{} //{command1; command2} END{}’ file
 [-F|-f|-v]   大參數,-F指定分隔符,-f調用腳本,-v定義變量 var=value
'  '          引用代碼塊
BEGIN   初始化代碼塊,在對每一行進行處理以前,初始化代碼,主要是引用全局變量,設置FS分隔符
//           匹配代碼塊,能夠是字符串或正則表達式
{}           命令代碼塊,包含一條或多條命令
;          多條命令使用分號分隔
END      結尾代碼塊,在對每一行進行處理以後再執行的代碼塊,主要是進行最終計算或輸出結尾摘要信息
 
特殊要點:
$0           表示整個當前行
$1           每行第一個字段
NF          字段數量變量
NR          每行的記錄號,多文件記錄遞增
FNR        與NR相似,不過多文件記錄不遞增,每一個文件都從1開始
\t            製表符
\n           換行符
FS          BEGIN時定義分隔符
RS        輸入的記錄分隔符, 默認爲換行符(即文本是按一行一行輸入)
~            匹配,與==相比不是精確比較
!~           不匹配,不精確比較
==         等於,必須所有相等,精確比較
!=           不等於,精確比較
&&      邏輯與
||             邏輯或
+            匹配時表示1個或1個以上
/[0-9][0-9]+/   兩個或兩個以上數字
/[0-9][0-9]*/    一個或一個以上數字
FILENAME 文件名
OFS      輸出字段分隔符, 默認也是空格,能夠改成製表符等
ORS        輸出的記錄分隔符,默認爲換行符,即處理結果也是一行一行輸出到屏幕
-F'[:#/]'   定義三個分隔符
 
print & $0
print 是awk打印指定內容的主要命令
awk '{print}'  /etc/passwd   ==   awk '{print $0}'  /etc/passwd  
awk '{print " "}' /etc/passwd                                           //不輸出passwd的內容,而是輸出相同個數的空行,進一步解釋了awk是一行一行處理文本
awk '{print "a"}'   /etc/passwd                                        //輸出相同個數的a行,一行只有一個a字母
awk -F":" '{print $1}'  /etc/passwd 
awk -F: '{print $1; print $2}'   /etc/passwd                   //將每一行的前二個字段,分行輸出,進一步理解一行一行處理文本
awk  -F: '{print $1,$3,$6}' OFS="\t" /etc/passwd        //輸出字段1,3,6,以製表符做爲分隔符
 
-f指定腳本文件
awk -f script.awk  file
BEGIN{
FS=":"
}
{print $1}               //效果與awk -F":" '{print $1}'相同,只是分隔符使用FS在代碼自身中指定
 
awk 'BEGIN{X=0} /^$/{ X+=1 } END{print "I find",X,"blank lines."}' test 
I find 4 blank lines.
 ls -l|awk 'BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is",sum}'                    //計算文件大小
total size is 17487
 
-F指定分隔符
$1 指指定分隔符後,第一個字段,$3第三個字段, \t是製表符
一個或多個連續的空格或製表符看作一個定界符,即多個空格看作一個空格
awk -F":" '{print $1}'  /etc/passwd
awk -F":" '{print $1 $3}'  /etc/passwd                       //$1與$3相連輸出,不分隔
awk -F":" '{print $1,$3}'  /etc/passwd                       //多了一個逗號,$1與$3使用空格分隔
awk -F":" '{print $1 " " $3}'  /etc/passwd                  //$1與$3之間手動添加空格分隔
awk -F":" '{print "Username:" $1 "\t\t Uid:" $3 }' /etc/passwd       //自定義輸出  
awk -F: '{print NF}' /etc/passwd                                //顯示每行有多少字段
awk -F: '{print $NF}' /etc/passwd                              //將每行第NF個字段的值打印出來
 awk -F: 'NF==4 {print }' /etc/passwd                       //顯示只有4個字段的行
awk -F: 'NF>2{print $0}' /etc/passwd                       //顯示每行字段數量大於2的行
awk '{print NR,$0}' /etc/passwd                                 //輸出每行的行號
awk -F: '{print NR,NF,$NF,"\t",$0}' /etc/passwd      //依次打印行號,字段數,最後字段值,製表符,每行內容
awk -F: 'NR==5{print}'  /etc/passwd                         //顯示第5行
awk -F: 'NR==5 || NR==6{print}'  /etc/passwd       //顯示第5行和第6行
route -n|awk 'NR!=1{print}'                                       //不顯示第一行
 
//匹配代碼塊
//純字符匹配   !//純字符不匹配    ~//字段值匹配    !~//字段值不匹配   ~/a1|a2/字段值匹配a1或a2   
awk '/mysql/' /etc/passwd
awk '/mysql/{print }' /etc/passwd
awk '/mysql/{print $0}' /etc/passwd                   //三條指令結果同樣
awk '!/mysql/{print $0}' /etc/passwd                  //輸出不匹配mysql的行
awk '/mysql|mail/{print}' /etc/passwd
awk '!/mysql|mail/{print}' /etc/passwd
awk -F: '/mail/,/mysql/{print}' /etc/passwd         //區間匹配
awk '/[2][7][7]*/{print $0}' /etc/passwd               //匹配包含27爲數字開頭的行,如27,277,2777...
awk -F: '$1~/mail/{print $1}' /etc/passwd           //$1匹配指定內容才顯示
awk -F: '{if($1~/mail/) print $1}' /etc/passwd     //與上面相同
awk -F: '$1!~/mail/{print $1}' /etc/passwd          //不匹配
awk -F: '$1!~/mail|mysql/{print $1}' /etc/passwd        
 
IF語句
必須用在{}中,且比較內容用()擴起來
awk -F: '{if($1~/mail/) print $1}' /etc/passwd                                       //簡寫
awk -F: '{if($1~/mail/) {print $1}}'  /etc/passwd                                   //全寫
awk -F: '{if($1~/mail/) {print $1} else {print $2}}' /etc/passwd            //if...else...
 
 
條件表達式
==   !=   >   >=  
awk -F":" '$1=="mysql"{print $3}' /etc/passwd  
awk -F":" '{if($1=="mysql") print $3}' /etc/passwd          //與上面相同 
awk -F":" '$1!="mysql"{print $3}' /etc/passwd                 //不等於
awk -F":" '$3>1000{print $3}' /etc/passwd                      //大於
awk -F":" '$3>=100{print $3}' /etc/passwd                     //大於等於
awk -F":" '$3<1{print $3}' /etc/passwd                            //小於
awk -F":" '$3<=1{print $3}' /etc/passwd                         //小於等於
 
邏輯運算符
&& || 
awk -F: '$1~/mail/ && $3>8 {print }' /etc/passwd         //邏輯與,$1匹配mail,而且$3>8
awk -F: '{if($1~/mail/ && $3>8) print }' /etc/passwd
awk -F: '$1~/mail/ || $3>1000 {print }' /etc/passwd       //邏輯或
awk -F: '{if($1~/mail/ || $3>1000) print }' /etc/passwd 
 
數值運算
awk -F: '$3 > 100' /etc/passwd    
awk -F: '$3 > 100 || $3 < 5' /etc/passwd  
awk -F: '$3+$4 > 200' /etc/passwd
awk -F: '/mysql|mail/{print $3+10}' /etc/passwd                    //第三個字段加10打印 
awk -F: '/mysql/{print $3-$4}' /etc/passwd                             //減法
awk -F: '/mysql/{print $3*$4}' /etc/passwd                             //求乘積
awk '/MemFree/{print $2/1024}' /proc/meminfo                  //除法
awk '/MemFree/{print int($2/1024)}' /proc/meminfo           //取整
 
輸出分隔符OFS
awk '$6 ~ /FIN/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt
awk '$6 ~ /WAIT/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt        
//輸出字段6匹配WAIT的行,其中輸出每行行號,字段4,5,6,並使用製表符分割字段
 
輸出處理結果到文件
①在命令代碼塊中直接輸出    route -n|awk 'NR!=1{print > "./fs"}'   
②使用重定向進行輸出           route -n|awk 'NR!=1{print}'  > ./fs
 
格式化輸出
netstat -anp|awk '{printf "%-8s %-8s %-10s\n",$1,$2,$3}' 
printf表示格式輸出
%格式化輸出分隔符
-8長度爲8個字符
s表示字符串類型
打印每行前三個字段,指定第一個字段輸出字符串類型(長度爲8),第二個字段輸出字符串類型(長度爲8),
第三個字段輸出字符串類型(長度爲10)
netstat -anp|awk '$6=="LISTEN" || NR==1 {printf "%-10s %-10s %-10s \n",$1,$2,$3}'
netstat -anp|awk '$6=="LISTEN" || NR==1 {printf "%-3s %-10s %-10s %-10s \n",NR,$1,$2,$3}'
 
IF語句
awk -F: '{if($3>100) print "large"; else print "small"}' /etc/passwd
small
small
small
large
small
small
awk -F: 'BEGIN{A=0;B=0} {if($3>100) {A++; print "large"} else {B++; print "small"}} END{print A,"\t",B}' /etc/passwd 
                                                                                                                  //ID大於100,A加1,不然B加1
awk -F: '{if($3<100) next; else print}' /etc/passwd                         //小於100跳過,不然顯示
awk -F: 'BEGIN{i=1} {if(i<NF) print NR,NF,i++ }' /etc/passwd   
awk -F: 'BEGIN{i=1} {if(i<NF) {print NR,NF} i++ }' /etc/passwd
另外一種形式
awk -F: '{print ($3>100 ? "yes":"no")}'  /etc/passwd 
awk -F: '{print ($3>100 ? $3":\tyes":$3":\tno")}'  /etc/passwd
 
while語句
awk -F: 'BEGIN{i=1} {while(i<NF) print NF,$i,i++}' /etc/passwd 
7 root 1
7 x 2
7 0 3
7 0 4
7 root 5
7 /root 6
 
數組
netstat -anp|awk 'NR!=1{a[$6]++} END{for (i in a) print i,"\t",a[i]}'
netstat -anp|awk 'NR!=1{a[$6]++} END{for (i in a) printf "%-20s %-10s %-5s \n", i,"\t",a[i]}'
9523                               1     
9929                               1     
LISTEN                            6     
7903                               1     
3038/cupsd                   1     
7913                               1     
10837                             1     
9833                               1     
 
應用1
awk -F: '{print NF}' helloworld.sh                                                       //輸出文件每行有多少字段
awk -F: '{print $1,$2,$3,$4,$5}' helloworld.sh                                 //輸出前5個字段
awk -F: '{print $1,$2,$3,$4,$5}' OFS='\t' helloworld.sh                 //輸出前5個字段並使用製表符分隔輸出
awk -F: '{print NR,$1,$2,$3,$4,$5}' OFS='\t' helloworld.sh           //製表符分隔輸出前5個字段,並打印行號
 
應用2
awk -F'[:#]' '{print NF}'  helloworld.sh                                                  //指定多個分隔符: #,輸出每行多少字段
awk -F'[:#]' '{print $1,$2,$3,$4,$5,$6,$7}' OFS='\t' helloworld.sh   //製表符分隔輸出多字段
 
應用3
awk -F'[:#/]' '{print NF}' helloworld.sh                                               //指定三個分隔符,並輸出每行字段數
awk -F'[:#/]' '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12}' helloworld.sh     //製表符分隔輸出多字段
 
應用4
計算/home目錄下,普通文件的大小,使用KB做爲單位
ls -l|awk 'BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is:",sum/1024,"KB"}'
ls -l|awk 'BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is:",int(sum/1024),"KB"}'         //int是取整的意思
 
應用5
統計netstat -anp 狀態爲LISTEN和CONNECT的鏈接數量分別是多少
netstat -anp|awk '$6~/LISTEN|CONNECTED/{sum[$6]++} END{for (i in sum) printf "%-10s %-6s %-3s \n", i," ",sum[i]}'
 
應用6
統計/home目錄下不一樣用戶的普通文件的總數是多少?
ls -l|awk 'NR!=1 && !/^d/{sum[$3]++} END{for (i in sum) printf "%-6s %-5s %-3s \n",i," ",sum[i]}'   
mysql        199 
root           374 
統計/home目錄下不一樣用戶的普通文件的大小總size是多少?
ls -l|awk 'NR!=1 && !/^d/{sum[$3]+=$5} END{for (i in sum) printf "%-6s %-5s %-3s %-2s \n",i," ",sum[i]/1024/1024,"MB"}'
 
應用7
輸出成績表
awk 'BEGIN{math=0;eng=0;com=0;printf "Lineno.   Name    No.    Math   English   Computer    Total\n";printf "------------------------------------------------------------\n"}{math+=$3; eng+=$4; com+=$5;printf "%-8s %-7s %-7s %-7s %-9s %-10s %-7s \n",NR,$1,$2,$3,$4,$5,$3+$4+$5} END{printf "------------------------------------------------------------\n";printf "%-24s %-7s %-9s %-20s \n","Total:",math,eng,com;printf "%-24s %-7s %-9s %-20s \n","Avg:",math/NR,eng/NR,com/NR}' test0

[root@localhost home]# cat test0 
Marry   2143 78 84 77
Jack    2321 66 78 45
Tom     2122 48 77 71
Mike    2537 87 97 95
Bob     2415 40 57 62

 
awk手冊 http://www.chinaunix.net/old_jh/7/16985.html
相關文章
相關標籤/搜索