linux命令終極系列awk



AWK man 手冊:http://man.linuxde.net/awk

AWK編程的內容極多,這里只羅列簡單常用的用法,更多請參考 http://www.gnu.org/software/gawk/manual/gawk.html


簡介

什么是awk? 

        awk是一個強大的文本分析工具,相對於grep的查找,sed的編輯,awk在其對數據分析並生成報告時,顯得尤為強大。簡單來說,awk就是把文件逐行的讀入,以空格為默認分隔符將每行切片,切開的部分再進行各種分析處理。即awk處理過程: 依次對每一行進行處理,然后輸出。

        awk有3個不同版本: awk、nawk和gawk,未作特別說明,一般指gawk,gawk 是 AWK 的 GNU 版本。

        awk其名稱得自於它的創始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首個字母。正是這三個人創造了awk---一個優秀的樣式掃描與處理工具。實際上 AWK 的確擁有自己的語言: AWK 程序設計語言, 三位創建者已將它正式定義為“樣式掃描和處理語言”。 它允許您創建簡短的程序,這些程序讀取輸入文件、為數據排序、處理數據、對輸入執行計算以及生成報表,還有無數其他的功能。


AWK的功能是什么?

        與sed和grep很相似,awk是一種樣式掃描與處理工具。但其功能卻大大強於sed和grep。awk提供了極其強大的功能:它幾乎可以完成grep和sed所能完成的全部工作,同時,它還可以可以進行樣式裝入、流控制、數學運算符、進程控制語句甚至於內置的變量和函數。它具備了一個完整的語言所應具有的幾乎所有精美特性。實際上,awk的確擁有自己的語言:awk程序設計語言,awk的三位創建者已將它正式定義為:樣式掃描和處理語言。

為什么使用awk?

        使用awk的第一個理由是基於文本的樣式掃描和處理是我們經常做的工作,awk所做的工作有些象數據庫,但與數據庫不同的是,它處理的是文本文件,這些文件沒有專門的存儲格式,普通的人們就能編輯、閱讀、理解和處理它們。而數據庫文件往往具有特殊的存儲格式,這使得它們必須用數據庫處理程序來處理它們。既然這種類似於數據庫的處理工作我們經常會遇到,我們就應當找到處理它們的簡便易行的方法,UNIX有很多這方面的工具,例如sed 、grep、sort以及find等等,awk是其中十分優秀的一種。 

        使用awk的第二個理由是awk是一個簡單的工具,當然這是相對於其強大的功能來說的。的確,UNIX有許多優秀的工具,例如UNIX天然的開發工具C語言及其延續C++就非常的優秀。但相對於它們來說,awk完成同樣的功能要方便和簡捷得多。這首先是因為awk提供了適應多種需要的解決方案:從解決簡單問題的awk命令行到復雜而精巧的awk程序設計語言,這樣做的好處是,你可以不必用復雜的方法去解決本來很簡單的問題。例如,你可以用一個命令行解決簡單的問題,而C不行,即使一個再簡單的程序,C語言也必須經過編寫、編譯的全過程。其次,awk本身是解釋執行的,這就使得awk程序不必經過編譯的過程,同時,這也使得它與shell script程序能夠很好的契合。最后,awk本身較C語言簡單,雖然awk吸收了C語言很多優秀的成分,熟悉C語言會對學習awk有很大的幫助,但awk本身不須要會使用C語言――一種功能強大但需要大量時間學習才能掌握其技巧的開發工具。
基於以上理由,再加上awk強大的功能,我們有理由說,如果你要處理與文本樣式掃描相關的工作,awk應該是你的第一選擇。在這里有一個可遵循的一般原則:如果你用普通的shell工具或shell script有困難的話,試試awk,如果awk仍不能解決問題,則便用C語言,如果C語言仍然失敗,則移至C++。 

AWK調用方式

有三種方式調用awk

1.AWK命令行方式  
awk [-F field-separator] 'commands' input-file(s)
其中,commands 是真正awk命令,[-F域分隔符]是可選的。 input-file(s) 是待處理的文件。
在awk中,文件的每一行中,由域分隔符分開的每一項稱為一個域。通常,在不指名-F域分隔符的情況下,默認的域分隔符是空格。

2.shell腳本方式
將一段awk程序寫入文本文件,然后在它的第一行加上:
#!/bin/awk -f
並賦予這個文本文件以執行的權限。
$awk腳本文本名 待處理文件

3.使用-f選項調用awk程序。awk允許將一段awk程序寫入一個文本文件,然后在awk命令行中用-f選項調用並執行這段程序。
awk -f awk-script-file input-file(s)
其中,-f選項加載awk-script-file中的awk腳本,input-file(s)跟上面的是一樣的。

AWK語法

 基本語法
awk [opion] 'awk_script' input_file1 [input_file2 ...]
awk的常用選項option有:
-F fs : 使用fs作為輸入記錄的字段分隔符,如果省略該選項,awk使用環境變量IFS的值
-f filename : 從文件filename中讀取awk_script
-v var=value : 為awk_script設置變量

awk '{pattern + action}' {filenames}

awk [-F|-f|-v] ‘BEGIN{} //{command1; command2} END{}’ file
[-F|-f|-v] 大參數,-F指定分隔符,-f調用腳本,-v定義變量 var=value
' ' 引用代碼塊
BEGIN 初始化代碼塊,在對每一行進行處理之前,初始化代碼,主要是引用全局變量,設置FS分隔符
// 匹配代碼塊,可以是字符串或正則表達式
{} 命令代碼塊,包含一條或多條命令
; 多條命令使用分號分隔
END 結尾代碼塊,在對每一行進行處理之后再執行的代碼塊,主要是進行最終計算或輸出結尾摘要信息
file awk的輸入文件,可以有多個。注意:awk不會修改輸入文件。如果沒有指定輸入文件,那么默認為標准輸入(屏幕)

awk的變量

awk提供兩種變量:
(1) 內置變量
  在引用時,不需要加$
(2) 自定義變量
        awk中引用自定義變量必須在它前面加上標志符"$"。awk根據其在awk中第一次出現的形式和上下文確定其具體的數據類型。當變量類型不確定時,awk默認其為字符串類型。這里有一個技巧:如果你要讓你的awk程序知道你所使用的變量的明確類型,你應當在在程序中給它賦初值。

awk的記錄,字段與內置變量
 1. 記錄與字段
在 awk中,缺省的情況下總是將文本文件中的一行視為一個記錄,而將一行中的某一部分作為記錄中的一個字段。為了操作這些不同的字段,awk借用shell 的方法,用$1,$2,$3...這樣的方式來順序地表示行(記錄)中的不同字段。特殊地,awk用$0表示整個行(記錄)。不同的字段之間是用稱作分隔 符的字符分隔開的。系統默認的分隔符是空格。awk允許在命令行中用-F re的形式來改變這個分隔符。事實上,awk用一個內置的變量FS來記憶這個分隔符
  2. 內置變量
  
awk中有好幾個這樣的內置變量,例如,記錄分隔符變量RS、當前工作的記錄數NR等等。
  示例:顯示文件本文件testAwk中第3行到第6行,以字符%分隔的第一個字段,第三個字段:
 awk -F % 'NR==3,NR==6 {printf $1  $3}' testAwk
   
  示例:殺死系統中所有top進程
    ps -ef|grep " top" |grep -v "grep"|awk '{printf $2}'|xargs kill -9
    或
    kill -9 `ps -ef|grep " top" |grep -v "grep"|awk '{printf $2}'`


$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'[:#/]' 定義三個分隔符

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更加好用,代碼更易懂。

格式化字符串輸出(sprintf使用):

其中格式化字符串包括兩部分內容: 一部分是正常字符, 這些字符將按原樣輸出; 另一部分是格式化規定字符, 以"%"開始, 后跟一個或幾個規定字符,用來確定輸出內容格式。

格式符 說明
%d 十進制有符號整數
%u 十進制無符號整數
%f 浮點數
%s 字符串
%c 單個字符
%p 指針的值
%e 指數形式的浮點數
%x %X 無符號以十六進制表示的整數
%o 無符號以八進制表示的整數
%g 自動選擇合適的表示法

[chengmo@centos5 ~]$ awk 'BEGIN{n1=124.113;n2=-1.224;n3=1.2345; printf("%.2f,%.2u,%.2g,%X,%o\n",n1,n2,n3,n1,n1);}'
124.11,18446744073709551615,1.2,7C,174

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}'

字符轉換: echo "65" |awk '{printf "%c\n",$0}' 輸出A
awk 'BEGIN {printf "%f\n",999}' 輸出999.000000
格式化輸出:awk '{printf "%-15s %s\n",$1,$3}' temp 將第一個域全部左對齊顯示

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 語句

if(表達式) #if ( Variable in Array )
語句1
else
語句2

格式中"語句1"可以是多個語句,如果你為了方便Unix awk判斷也方便你自已閱讀,你最好將多個語句用{}括起來。
Unix awk分枝結構允許嵌套,其格式為:

if (expression) {
statement;
statement;
... ...
}

if (expression) {
statement;
} else {
statement2;
}

if (expression) {
statement1;
} else if (expression1) {
statement2;
} else {
statement3;
}

[chengmo@localhost nginx]# awk 'BEGIN{
test=100;
if(test>90)
{
print "very good";
}
else if(test>60)
{
print "good";
}
else
{
print "no pass";
}
}'

very good
每條命令語句后面可以用“;”號結尾。

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: '{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="" <="" span="" style="word-wrap: break-word;">
awk -F: 'BEGIN{i=1} {if(i<nf) {print="" nr,nf}="" i++="" }'="" etc="" passwd<="" span="" style="word-wrap: break-word;">
另一種形式
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 ="" <="" div="" style="word-wrap: break-word;">
7 root 1
7 x 2
7 0 3
7 0 4
7 root 5
7 /root 6

統計某個文件夾下的文件占用的字節數,過濾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

循環語句(while,for,do)

awk的循環基本結構

For (element in array) print array[element]
awk 'BEGIN {record="123#456#789";split(record,myarray,"#")}
END { for (i in myarray) {print myarray[i]} }

1.while語句

格式:
while(表達式)
{語句}

例子:
[chengmo@localhost nginx]# awk 'BEGIN{
test=100;
total=0;
while(i<=test)
{
total+=i;
i++;
}
print total;
}'
5050

2.for 循環

for循環有兩種格式:

格式1:
for(變量 in 數組)
{語句}

例子:
[chengmo@localhost nginx]# awk 'BEGIN{
for(k in ENVIRON)
{
print k"="ENVIRON[k];
}
}'

AWKPATH=.:/usr/share/awk
OLDPWD=/home/web97
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SELINUX_LEVEL_REQUESTED=
SELINUX_ROLE_REQUESTED=
LANG=zh_CN.GB2312
。。。。。。
說明:ENVIRON 是awk常量,是子典型數組。

格式2:
for(變量;條件;表達式)
{語句}

例子:
[chengmo@localhost nginx]# awk 'BEGIN{
total=0;
for(i=0;i<=100;i++)
{
total+=i;
}
print total;
}'
5050

3.do循環
格式:
do
{語句}while(條件)

例子:
[chengmo@localhost nginx]# awk 'BEGIN{
total=0;
i=0;
do
{
total+=i;
i++;
}while(i<=100)
print total;
}'
5050
以上為awk流程控制語句,從語法上面大家可以看到,與c語言是一樣的。
有了這些語句,其實很多shell程序都可以交給awk,而且性能是非常快的。
break 當 break 語句用於 while 或 for 語句時,導致退出程序循環。
continue 當 continue 語句用於 while 或 for 語句時,使程序循環移動到下一個迭代。
next 能能夠導致讀入下一個輸入行,並返回到腳本的頂部。這可以避免對當前輸入行執行其他的操作過程。
exit 語句使主輸入循環退出並將控制轉移到END,如果END存在的話。如果沒有定義END規則,或在END中應用exit語句,則終止腳本的執行。

條件表達式

==   !=   >   >=    
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,並使用制表符分割字段

輸出處理結果到文件

1. 在命令代碼塊中直接輸出  route -n|awk 'NR!=1{print > "./fs"}'     
2. 使用重定向進行輸出 route -n|awk 'NR!=1{print}' > ./fs

數組

因為awk中數組的下標可以是數字和字母,數組的下標通常被稱為關鍵字(key)。值和關鍵字都存儲在內部的一張針對key/value應用hash的表格里。由於hash不是順序存儲,因此在顯示數組內容時會發現,它們並不是按照你預料的順序顯示出來的。數組和變量一樣,都是在使用時自動創建的,awk也同樣會自動判斷其存儲的是數字還是字符串。一般而言,awk中的數組用來從記錄中收集信息,可以用於計算總和、統計單詞以及跟蹤模板被匹配的次數等等。

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

顯示/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
......

其他awk用法

向一行awk命令傳值:
awk '{if ($5
who | awk '{if ($1==user) print $1 " are in " $2 ' user=$LOGNAME 使用環境變量

awk腳本命令:
開頭使用 !/bin/awk -f ,如果沒有這句話自含腳本將不能執行,
例子:
!/bin/awk -f
# all comment lines must start with a hash '#'
# name: student_tot.awk
# to call: student_tot.awk grade.txt
# prints total and average of club student points
# print a header first
BEGIN
{
print "Student Date Member No. Grade Age Points Max"
print "Name Joined Gained Point Available"
print"========================================================="
}
# let's add the scores of points gained
(tot+=$6);
# finished processing now let's print the total and average point
END
{
print "Club student total points :" tot
print "Average Club Student points :" tot/N
}

awk腳本

awk腳本可以由一條或多條awk_cmd組成,對於多個awk_cmd,一個awk_cmd完成后,應該另起一行,以便進行隔。  awk_cmd由兩部分組成: awk_pattern { actions } 另外,在awk命令中直接使用awk_script時,awk_script也可以被分成多行書寫,但必須確保整個awk_script單引號括起來。 awk命令的一般形式: awk ' BEGIN { actions } awk_pattern1 { actions } ............ awk_patternN { actions } END { actions } ' inputfile 其中 BEGIN { actions } 和 END { actions } 是可選的。 在awk腳本中可以使用AWK本身內置變量,如下: ARGC 命令行變元個數ARGV 命令行變元數組FILENAME 當前輸入文件名FNR 當前文件中的記錄號FS 輸入域分隔符,默認為一個空格RS 輸入記錄分隔符NF 當前記錄里域個數NR 到目前為止記錄數OFS 輸出域分隔符ORS 輸出記錄分隔符

AWK腳本的運行過程:

awk的流程控制
BEGIN和END:
任何在BEGIN之后列出的操作(在{}內)將在awk開始掃描輸入之前執行,而END之后列出的操作將在掃描完全部的輸入之后執行。因此,通常使用BEGIN來顯示變量和預置(初始化)變量,使用END來輸出最終結果。
例:累計銷售文件xs中的銷售金額(假設銷售金額在記錄的第三字段):

$awk
>'BEGIN { FS=":";print "統計銷售金額";total=0}
>{print $3;total=total+$3;}
>END {printf "銷售金額總計:%.2f",total}' sx
(注:>是shell提供的第二提示符,如要在shell程序awk語句和awk語言中換行,則需在行尾加反斜杠\)
在這里,BEGIN預置了內部變量FS(字段分隔符)和自定義變量total,同時在掃描之前顯示出輸出行頭。而END則在掃描完成后打印出總合計。

 如果BEGIN 區塊存在,awk執行它指定的actions。  awk從輸入文件中讀取一行,稱為一條輸入記錄。(如果輸入文件省略,將從標准輸入讀取) ③ awk將讀入的記錄分割成字段,將第1個字段放入變量$1中,第2個字段放入$2,以此類推。$0表示整條記錄。字段分隔符使用shell環境變量IFS或由參數指定。  把當前輸入記錄依次與每一個awk_cmd中awk_pattern比較,看是否匹配,如果相匹配,就執行對應的actions。如果不匹配,就跳過對應的actions,直到比較完所有的awk_cmd。  當一條輸入記錄比較了所有的awk_cmd后,awk讀取輸入的下一行,繼續重復步驟③和④,這個過程一直持續,直到awk讀取到文件尾。  當awk讀完所有的輸入行后,如果存在END,就執行相應的actions。


1)input_file可以是一個文件或者多個文件的列表,awk將按順序處理列表中的每個文件。 2)一條awk_cmdawk_pattern可以省略,省略時不對輸入記錄進行匹配比較就執行相應的actions。一條awk_cmd的actions 也可以省略,省略時默認的動作為打印當前輸入記錄,即{print $0} 。一條awk_cmd中的awk_pattern和actions不能同時省略 3) BEGIN區塊END區塊別位於awk_script的開頭和結尾awk_script中只有END區塊或者只有BEGIN區塊是被允許的。如果awk_script中只有BEGIN { actions } ,awk不會讀取input_file。 4) awk把輸入文件的數據讀入內存,然后操作內存中的輸入數據副本,awk不會修改輸入文件的內容。 5) awk的總是輸出到標准輸出,如果想讓awk輸出到文件,可以使用重定向。

awk_pattern

awk_pattern模式部分決定actions動作部分何時觸發及觸發actions

awk_pattern可以是以下幾種類型:

1) 正則表達式用作awk_pattern: /regexp/     注意,正則表達式regexp必須被/包起來     awk中正則表達式匹配操作中經常用到的字符:     \ ^ $ . [] | () * // :通用的regexp元字符     + : 匹配其前的單個字符一次以上,是awk自有的元字符,不適用於grep或sed等     ? : 匹配其前的單個字符1次或0次,是awk自有的元字符,不適用於grep或sed等     關於正則表達式的更多內容請參《正則表達式     舉例:     awk '/ *\$0\.[0-9][0-9].*/' input_file     比如,行內容為$0.99. helllo的行就可以和上面的正則表達式相配 2) 布爾表達式用作awk_pattern,表達式成立時,觸發相應的actions執行。     ① 表達式中可以使用變量(如字段變量$1,$2等)和/regexp/     ② 布爾表達式中的操作符:        關系操作符: < > <= >= == !=        匹配操作符: value ~ /regexp/ 如果value匹配/regexp/,則返回真        value !~ /regexp/ 如果value不匹配/regexp/,則返回真         舉例: awk '$2 > 10 {print "ok"}' input_file         awk '$3 ~ /^d/ {print "ok"}' input_file     ③ &&(與) 和 ||(或) 可以連接兩個/regexp/或者布爾表達式,構成混合表達式!(非) 可以用於布爾表達式或者/regexp/之前。        舉例: awk '($1 < 10 ) && ($2 > 10) {print $0 "ok"}' input_file        awk '/^d/ || /x${print $0 "ok"}' input_file     ④ 其它表達式用作awk_script,如賦值表達式等        舉例:        awk '(tot+=$6); END{print "total points :" tot }' input_file // 分號不能省略        awk 'tot+=$6 {print $0} END{print "total points :" tot }' input_file // 與上面等效    當使用賦值表達式時,表示如果賦值后的變量是數字的話,如果為非0,就匹配,否則不匹配;如果為字符串的話,非空就為匹配,否則不匹配。

awk內置字符串函數: gsub(r,s)           在整個$0中用s替代r awk 'gsub(/name/,"xingming") {print $0}' temp gsub(r,s,t)         在整個t中用s替代r index(s,t)          返回s中字符串t的第一位置 awk 'BEGIN {print index("Sunny","ny")}' temp     返回4 length(s)           返回s的長度 match(s,r)          測試s是否包含匹配r的字符串 awk '$1=="J.Lulu" {print match($1,"u")}' temp    返回4 split(s,a,fs)       在fs上將s分成序列a awk 'BEGIN {print split("12#345#6789",myarray,"#")"' 返回3,同時myarray[1]="12", myarray[2]="345", myarray[3]="6789" sprint(fmt,exp)     返回經fmt格式化后的exp sub(r,s)   從$0中最左邊最長的子串中用s代替r(只更換第一遇到的匹配字符串) substr(s,p)         返回字符串s中從p開始的后綴部分 substr(s,p,n)       返回字符串s中從p開始長度為n的后綴部分

awk字符串連接操作 [chengmo@centos5 ~]$ awk 'BEGIN{a="a";b="b";c=(a""b);print c}'      

AWK 內置函數

awk內置函數,主要分以下3種類似:算數函數、字符串函數、其它一般函數、時間函數

1.以下示例了printf函數,它與C語言相似,如下,顯示testAwk文件中行號與第1個字段:
$awk '{printf"%03d%s\n",NR,$1}' testAwk
2. 顯示文本文件mydoc匹配(含有)字符串"sun"的所有行
$awk '/sun/{print}' mydoc
3.由於顯示整個記錄(全行)是awk的缺省動作,因此可以省略action項
$awk '/sun/' mydoc
4.示第一個匹配Sun或sun的行與第一個匹配Moon或moon的行之間的行,並顯示到標准輸出上:
$awk '/[Ss]un/,/[Mm]oon/ {print}' myfile
5.下面的示例顯示了內置變量和內置函數length()的使用:
$awk 'length($0)>80 {print NR}' myfile
6.UNIX中的用戶進行安全性檢查。
方法是考察/etc下的passwd文件,檢查其中的passwd字段(第二字段)是否為為"*"
如不為"*",則表示該用戶沒有設置密碼,顯示出這些用戶名(第一字段)。
#awk -F: '$2=="" {printf("%s no password!\n",$1' /etc/passwd
在這個示例中,passwd文件的字段分隔符是“:”,因此,必須用-F:來更改默認的字段分隔符,
這個示例中也涉及到了內置函數printf的使用

一、算術函數:

以下算術函數執行與 C 語言中名稱相同的子例程相同的操作:

函數名 說明
atan2( y, x ) 返回 y/x 的反正切。
cos( x ) 返回 x 的余弦;x 是弧度。
sin( x ) 返回 x 的正弦;x 是弧度。
exp( x ) 返回 x 冪函數。
log( x ) 返回 x 的自然對數。
sqrt( x ) 返回 x 平方根。
int( x ) 返回 x 的截斷至整數的值。
rand( ) 返回任意數字 n,其中 0 <= n < 1。
srand( [Expr] ) 將 rand 函數的種子值設置為 Expr 參數的值,或如果省略 Expr 參數則使用某天的時間。返回先前的種子值。

舉例說明:
[chengmo@centos5 ~]$ awk 'BEGIN{OFMT="%.3f";fs=sin(1);fe=exp(10);fl=log(10);fi=int(3.1415);print fs,fe,fl,fi;}'
0.841 22026.466 2.303 3

OFMT 設置輸出數據格式是保留3位小數

獲得隨機數:
[chengmo@centos5 ~]$ awk 'BEGIN{srand();fr=int(100*rand());print fr;}'
78
[chengmo@centos5 ~]$ awk 'BEGIN{srand();fr=int(100*rand());print fr;}'
31
[chengmo@centos5 ~]$ awk 'BEGIN{srand();fr=int(100*rand());print fr;}'
41

二、字符串函數:


函數 說明
gsub( Ere, Repl, [ In ] ) 除了正則表達式所有具體值被替代這點,它和 sub 函數完全一樣地執行,。
sub( Ere, Repl, [ In ] ) 用 Repl 參數指定的字符串替換 In 參數指定的字符串中的由 Ere 參數指定的擴展正則表達式的第一個具體值。sub 函數返回替換的數量。出現在 Repl 參數指定的字符串中的 &(和符號)由 In 參數指定的與 Ere 參數的指定的擴展正則表達式匹配的字符串替換。如果未指定 In 參數,缺省值是整個記錄($0 記錄變量)。
index( String1, String2 ) 在由 String1 參數指定的字符串(其中有出現 String2 指定的參數)中,返回位置,從 1 開始編號。如果 String2 參數不在 String1 參數中出現,則返回 0(零)。
length [(String)] 返回 String 參數指定的字符串的長度(字符形式)。如果未給出 String 參數,則返回整個記錄的長度($0 記錄變量)。
blength [(String)] 返回 String 參數指定的字符串的長度(以字節為單位)。如果未給出 String 參數,則返回整個記錄的長度($0 記錄變量)。
substr( String, M, [ N ] ) 返回具有 N 參數指定的字符數量子串。子串從 String 參數指定的字符串取得,其字符以 M 參數指定的位置開始。M 參數指定為將 String 參數中的第一個字符作為編號 1。如果未指定 N 參數,則子串的長度將是 M 參數指定的位置到 String 參數的末尾 的長度。
match( String, Ere ) 在 String 參數指定的字符串(Ere 參數指定的擴展正則表達式出現在其中)中返回位置(字符形式),從 1 開始編號,或如果 Ere 參數不出現,則返回 0(零)。RSTART 特殊變量設置為返回值。RLENGTH 特殊變量設置為匹配的字符串的長度,或如果未找到任何匹配,則設置為 -1(負一)。
split( String, A, [Ere] ) 將 String 參數指定的參數分割為數組元素 A[1], A[2], . . ., A[n],並返回 n 變量的值。此分隔可以通過 Ere 參數指定的擴展正則表達式進行,或用當前字段分隔符(FS 特殊變量)來進行(如果沒有給出 Ere 參數)。除非上下文指明特定的元素還應具有一個數字值,否則 A 數組中的元素用字符串值來創建。
tolower( String ) 返回 String 參數指定的字符串,字符串中每個大寫字符將更改為小寫。大寫和小寫的映射由當前語言環境的 LC_CTYPE 范疇定義。
toupper( String ) 返回 String 參數指定的字符串,字符串中每個小寫字符將更改為大寫。大寫和小寫的映射由當前語言環境的 LC_CTYPE 范疇定義。
sprintf(Format, Expr, Expr, . . . ) 根據 Format 參數指定的 printf 子例程格式字符串來格式化 Expr 參數指定的表達式並返回最后生成的字符串。

Ere都可以是正則表達式

gsub,sub使用
[chengmo@centos5 ~]$ awk 'BEGIN{info="this is a test2010test!";gsub(/[0-9]+/,"!",info);print info}'
this is a test!test!

在 info中查找滿足正則表達式,/[0-9]+/ 用””替換,並且替換后的值,賦值給info 未給info值,默認是$0

查找字符串(index使用)
[wangsl@centos5 ~]$ awk 'BEGIN{info="this is a test2010test!";print index(info,"test")?"ok":"no found";}'
ok

未找到,返回0

正則表達式匹配查找(match使用)
[wangsl@centos5 ~]$ awk 'BEGIN{info="this is a test2010test!";print match(info,/[0-9]+/)?"ok":"no found";}'
ok

截取字符串(substr使用)
[wangsl@centos5 ~]$ awk 'BEGIN{info="this is a test2010test!";print substr(info,4,10);}'
s is a tes

從第 4個 字符開始,截取10個長度字符串

字符串分割(split使用)
[chengmo@centos5 ~]$ awk 'BEGIN{info="this is a test";split(info,tA," ");print length(tA);for(k in tA){print k,tA[k];}}'
4
4 test
1 this
2 is
3 a

分割info,動態創建數組tA,這里比較有意思,awk for …in 循環,是一個無序的循環。 並不是從數組下標1…n ,因此使用時候需要注意。

三、一般函數是:

函數 說明
close( Expression ) 用同一個帶字符串值的 Expression 參數來關閉由 print 或 printf 語句打開的或調用 getline 函數打開的文件或管道。如果文件或管道成功關閉,則返回 0;其它情況下返回非零值。如果打算寫一個文件,並稍后在同一個程序中讀取文件,則 close 語句是必需的。
system(Command ) 執行 Command 參數指定的命令,並返回退出狀態。等同於system 子例程。
Expression | getline [ Variable ] 從來自 Expression 參數指定的命令的輸出中通過管道傳送的流中讀取一個輸入記錄,並將該記錄的值指定給 Variable 參數指定的變量。如果當前未打開將 Expression 參數的值作為其命令名稱的流,則創建流。創建的流等同於調用 popen 子例程,此時 Command 參數取 Expression 參數的值且 Mode 參數設置為一個是 r 的值。只要流保留打開且 Expression 參數求得同一個字符串,則對 getline 函數的每次后續調用讀取另一個記錄。如果未指定 Variable 參數,則 $0 記錄變量和 NF 特殊變量設置為從流讀取的記錄。
getline [ Variable ] < Expression 從 Expression 參數指定的文件讀取輸入的下一個記錄,並將 Variable 參數指定的變量設置為該記錄的值。只要流保留打開且 Expression 參數對同一個字符串求值,則對 getline 函數的每次后續調用讀取另一個記錄。如果未指定 Variable 參數,則 $0 記錄變量和 NF 特殊變量設置為從流讀取的記錄。
getline [ Variable ] 將 Variable 參數指定的變量設置為從當前輸入文件讀取的下一個輸入記錄。如果未指定 Variable 參數,則 $0 記錄變量設置為該記錄的值,還將設置 NF、NR 和 FNR 特殊變量。

打開外部文件(close用法)
[chengmo@centos5 ~]$ awk 'BEGIN{while("cat /etc/passwd"|getline){print $0;};close("/etc/passwd");}'
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

逐行讀取外部文件(getline使用方法)
[chengmo@centos5 ~]$ awk 'BEGIN{while(getline < "/etc/passwd"){print $0;};close("/etc/passwd");}'
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

[chengmo@centos5 ~]$ awk 'BEGIN{print "Enter your name:";getline name;print name;}'
Enter your name:
chengmo
chengmo

調用外部應用程序(system使用方法)
[chengmo@centos5 ~]$ awk 'BEGIN{b=system("ls -al");print b;}'
total 42092
drwxr-xr-x 14 chengmo chengmo 4096 09-30 17:47 .
drwxr-xr-x 95 root root 4096 10-08 14:01 ..

b返回值,是執行結果。

四、時間函數

函數名 說明
mktime( YYYY MM DD HH MM SS[ DST]) 生成時間格式
strftime([format [, timestamp]]) 格式化時間輸出,將時間戳轉為時間字符串 
具體格式,見下表.
systime() 得到時間戳,返回從1970年1月1日開始到當前時間(不計閏年)的整秒數

創建指定時間(mktime使用)
[chengmo@centos5 ~]$ awk 'BEGIN{tstamp=mktime("2001 01 01 12 12 12");print strftime("%c",tstamp);}'
2001年01月01日 星期一 12時12分12秒

[chengmo@centos5 ~]$ awk 'BEGIN{tstamp1=mktime("2001 01 01 12 12 12");tstamp2=mktime("2001 02 01 0 0 0");print tstamp2-tstamp1;}'
2634468

求2個時間段中間時間差,介紹了strftime使用方法

[chengmo@centos5 ~]$ awk 'BEGIN{tstamp1=mktime("2001 01 01 12 12 12");tstamp2=systime();print tstamp2-tstamp1;}'
308201392

strftime日期和時間格式說明符

格式 描述
%a 星期幾的縮寫(Sun)
%A 星期幾的完整寫法(Sunday)
%b 月名的縮寫(Oct)
%B 月名的完整寫法(October)
%c 本地日期和時間
%d 十進制日期
%D 日期 08/20/99
%e 日期,如果只有一位會補上一個空格
%H 用十進制表示24小時格式的小時
%I 用十進制表示12小時格式的小時
%j 從1月1日起一年中的第幾天
%m 十進制表示的月份
%M 十進制表示的分鍾
%p 12小時表示法(AM/PM)
%S 十進制表示的秒
%U 十進制表示的一年中的第幾個星期(星期天作為一個星期的開始)
%w 十進制表示的星期幾(星期天是0)
%W 十進制表示的一年中的第幾個星期(星期一作為一個星期的開始)
%x 重新設置本地日期(08/20/99)
%X 重新設置本地時間(12:00:00)
%y 兩位數字表示的年(99)
%Y 當前月份
%Z 時區(PDT)
%% 百分號(%)

以上是awk常見 內置函數使用及說明,希望對大家有所幫助。

awk中的自定義函數

原始的awk並不提供函數功能,只有在nawk或較新的awk版本中才可以增加函數
awk函數的定義方法如下:

function 函數名(參數表){ 
函數體
}

        在gawk中允許將function省略為func,但其它版本的awk不允許。在 awk中調用函數比較簡單,其方法與C語言相似,但awk比C語言更為靈活,它不執行參數有效性檢查。換句話說,在你調用函數時,可以列出比函數預計(函 數定義中規定)的多或少的參數,多余的參數會被awk所忽略,而不足的參數,awk將它們置為缺省值0或空字符串,具體置為何值,將取決於參數的使用方 式。在函數中使用形如:return 返回值 格式的語句。

例: 下面的例子演示了函數的使用。在這個示例中,定義了一個名為print_header的函數,該函數調用了兩個參數FileName和 PageNum,FileName參數傳給函數當前使用的文件名,PageNum參數是當前頁的頁號。這個函數的功能是打印(顯示)出當前文件的文件名, 和當前頁的頁號。完成這個功能后,這個函數將返回下一頁的頁號。

nawk 
>'BEGIN{pageno=1;file=FILENAME
>pageno=print_header(file,pageno);#調用函數print_header
>printf("當前頁頁號是:%d\n",pageno);
>}
>#定義函數print_header
>function print_header(FileName,PageNum){
>printf("%s %d\n",FileName,PageNum); >PageNum++;return PageNUm;
>}
>}' myfile
執行這個程序將顯示如下內容:
myfile 1
當前頁頁號是:2

NR與FNR: QUOTE: A.awk對多輸入文件的執行順序是,先將代碼作用於第一個文件(一行行讀入),然后該重復的代碼又作用於第二個文件,再作用於第三個文件。 B.awk對多輸入文件的執行順序產生了行序號的問題。當第一個文件執行完,下次讀入第二個文件,那么第二個文件的第一行怎么算呢?如果又計為1的話,那不就兩個1了么?(因為第一個文件也有第一行)。這就是NR和FNR的問題。    NR :全局行數(第二個文件的第一行接着第一個文件尾行數順序計數)    FNR:當前文件自身的行數(不考慮前幾個輸入文件的自身行數及總數)          例如:data1.txt中有40行,data2.txt中有50行,那么awk ‘{}’ data1.txt data2.txt                   NR  的值依次為:1,2……40,41,42……90                    FNR的值依次為:1,2……40, 1, 2……50  getline函數說明: awk 的 getline語句用於簡單地讀取一條記錄。如果用戶有一個數據記錄類似兩個物理記錄,那么getline將尤其有用。它完成一般字段的分離(設置字段變量$0 FNR NF NR)。如果成功則返回1,失敗則返回0(到達文件尾)。 QUOTE: A.getline從整體上來說,應這么理解它的用法:             當其左右無重定向符 | 或 < 時,getline作用於當前文件,讀入當前文件的第一行給其后跟的變量              var 或$0(無變量);應該注意到,由於awk在處理getline之前已經讀入了一行,所以getline得到             的返回結果是隔行的。             當其左右有重定向符 | 或 < 時,getline則作用於定向輸入文件,由於該文件是剛打開,並沒有被             awk讀入一行,只是getline讀入,那么getline返回的是該文件的第一行,而不是隔行。 B.getline用法大致可分為三大類(每大類又分兩小類),即總共有6種用法。代碼如下: QUOTE: nawk ‘BEGIN{“cat data.txt”|getline d; print d}’ data2.txt  nawk ‘BEGIN{“cat data.txt”|getline; print $0}’ data2.txt nawk ‘BEGIN{getline d < “data.txt”; print d}’ data2.txt  nawk ‘BEGIN{getline < “data.txt”; print $0}’ data2.txt       以上四行代碼均實現“只打印data.txt文件的第一行”(若打印全部行,用循環) eg. nawk ‘BEGIN{FS=”:”;while(getline<”/etc/passwd”>0){print $1}}’ data.txt
QUOTE: nawk ‘{getline d; print d”#”$3}’ data.txt  awk首先讀入第一行,接着處理getline函數,然后把下一行指定給變量d,再先打印d,由於d后面有換行符,所以后面緊跟的#會覆蓋d,后面的$3同樣也會覆蓋d。 QUOTE: nawk ‘{getline; print $0”#”$3}’ data.txt awk首先讀入第一行接着處理getline函數,然后把下一行指定給$0,現在的$0已經是下一行內容,后面的#和$3(從$0中取)會覆蓋$0的內容。 在awk中,有時需要調用系統工具來完成awk不擅長的工作,awk提供的system命令可以用來執行,但收不到外部工具的輸出結果。好在可以運用getline來滿足這個需求。例如test.awk:{   datecommand="/bin/date -j -f \"%d/%b/%Y:%H:%M:%S\" " $olddatestr " \"+%Y%m%d %H%M%S\"";   datecommand | getline newdatestr    close(datecommand);} 外部命令需要awk占用一個文件描述符,而awk最多能打開的文件有一個上限,而且不大(比如說16),所以最后做一個close是好習慣。把命令串定義為一個變量也是為了close的時候方便

awk高級輸入輸出

1.讀取下一條記錄: 
        awk的next語句導致awk讀取下一個記錄並完成模式匹配,然后立即執行相應的操作。通常它用匹配的模式執行操作中的代碼。next導致這個記錄的任何額外匹配模式被忽略。 

2.簡單地讀取一條記錄 
        awk 的 getline語句用於簡單地讀取一條記錄。如果用戶有一個數據記錄類似兩個物理記錄,那么getline將尤其有用。它完成一般字段的分離(設置字段變 量$0 FNR NF NR)。如果成功則返回1,失敗則返回0(到達文件尾)。如果需簡單地讀取一個文件,則可以編寫以下代碼: 
例:示例getline的使用 
{while(getline==1) 

#process the inputted fields 


也可以使getline保存輸入數據在一個字段中,而不是通過使用getline variable的形式處理一般字段。當使用這種方式時,NF被置成0,FNR和NR被增值。 
用 戶也可以使用getline<"filename"方式從一個給定的文件中輸入數據,而不是從命令行所列內容輸入數據。此時,getline將完成 一般字段分離(設置字段變量$0和NF)。如果文件不存在,返回-1,成功,返回1,返回0表示失敗。用戶可以從給定文件中讀取數據到一個變量中,也可以 用stdin(標准輸入設備)或一個包含這個文件名的變量代替filename。值得注意的是當使用這種方式時不修改FNR和NR。 

另一種使用getline語句的方法是從UNIX命令接受輸入,例如下面的例子: 
例:示例從UNIX命令接受輸入 
{while("who -u"|getline) 

#process each line from the who command 



當然,也可以使用如下形式: 
"command" | getline variable 

3.關閉文件: 
        awk中允許在程序中關閉一個輸入或輸出文件,方法是使用awk的close語句。 
close("filename") 
filename可以是getline打開的文件(也可以是stdin,包含文件名的變量或者getline使用的確切命令)。或一個輸出文件(可以是stdout,包含文件名的變量或使用管道的確切命令)。 

4.輸出到一個文件: 
        awk中允許用如下方式將結果輸出到一個文件: 
printf("hello word!\n")>"datafile" 
或 
printf("hello word!\n")>>"datafile" 

5.輸出到一個命令 
        awk中允許用如下方式將結果輸出到一個命令: 
printf("hello word!\n")|"sort-t','"

awk與shell script混合編程

因 為awk可以作為一個shell命令使用,因此awk能與shell批處理程序很好的融合在一起,這給實現awk與shell程序的混合編程提供了可能。 實現混合編程的關鍵是awk與shell script之間的對話,換言之,就是awk與shell script之間的信息交流:awk從shell script中獲取所需的信息(通常是變量的值)、在awk中執行shell命令行、shell script將命令執行的結果送給awk處理以及shell script讀取awk的執行結果等等。 

1.awk讀取Shell script程序變量 
在awk中我們可以通過“'$變量名'”的方式讀取sell scrpit程序中的變量。 
例:在下面的示例中,我們將讀取sell scrpit程序中的變量Name,該變量存放的是文本myfile的撰寫者,awk將打印出這個人名。 
$cat writename 

# @(#) 




Name="張三" nawk 'BEGIN {name="'Name'";\ printf("\t%s\t撰寫者%s\n",FILENAME,name");}\ 
{...}END{...}' myfile 




2.將shell命令的執行結果送給awk處理 
作為信息傳送的一種方法,我們可以將一條shell命令的結果通過管道線(|)傳遞給awk處理: 
例:示例awk處理shell命令的執行結果 
$who -u | awk '{printf("%s正在執行%s\n",$2,$1)}' 
該命令將打印出注冊終端正在執行的程序名。 

3.shell script程序讀awk的執行結果 
為 了實現shell script程序讀取awk執行的結果,我們可以采取一些特殊的方法,例如我們可以用變量名=`awk語句`的形式將awk執行的結果存放入一個 shell script變量。當然也可以用管道線的方法將awk執行結果傳遞給shell script程序處理。 
例:作為傳送消息 的機制之一,UNIX提供了一個向其所有用戶傳送消息的命令wall(意思是write to all寫給所有用戶),該命令允許向所有工作中的用戶(終端)發送消息。為此,我們可以通過一段shell批處理程序wall.shell來模擬這一程序 (事實上比較老的版本中wall就是一段shell批處理程序: 

$cat wall.shell 

# @(#) wall.shell:發送消息給每個已注冊終端 

cat >/tmp/
#用戶錄入消息文本 who -u | awk '{print $2}' | while read tty  
do  
cat /tmp/
>$tty 
done 

在 這個程序里,awk接受who -u命令的執行結果,該命令打印出所有已注冊終端的信息,其中第二個字段是已注冊終端的設備名,因此用awk命令析出該設備名,然后用while read tty語句循環讀出這些文件名到變量(shell script變量)tty中,作為信息傳送的終結地址。 

4.在awk中執行shell命令行----嵌入函數system() 
system()是一個不適合字符或數字類型的嵌入函數,該函數的功能是處理作為參數傳遞給它的字符串。system對這個參數的處理就是將其作為命令處理,也就是說將其當作命令行一樣加以執行。這使得用戶在自己的awk程序需要時可以靈活地執行命令或腳本。 
例:下面的程序將使用system嵌入函數打印用戶編制好的報表文件,這個文件存放在名為myreport.txt的文件中。為簡約起見,我們只列出了其END部分: 



END {close("myreport.txt");system("lp myreport.txt");} 
在這個示例中,我們首先使用close語句關閉了文件myreport.txt文件,然后使用system嵌入函數將myreport.txt送入打印機打印。 

入門實例

假設last -n 5的輸出如下
[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/s

如果只是顯示/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}

應用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

附錄: 

1.awk的常規表達式元字符 
\ 換碼序列 
^ 在字符串的開頭開始匹配 
$ 在字符串的結尾開始匹配 
. 與任何單個字符串匹配 
[ABC] 與[]內的任一字符匹配 
[A-Ca-c] 與A-C及a-c范圍內的字符匹配(按字母表順序) 
[^ABC] 與除[]內的所有字符以外的任一字符匹配 
Desk|Chair 與Desk和Chair中的任一個匹配 
[ABC][DEF] 關聯。與A、B、C中的任一字符匹配,且其后要跟D、E、F中的任一個字符。 
* 與A、B或C中任一個出現0次或多次的字符相匹配 
+ 與A、B或C中任何一個出現1次或多次的字符相匹配 
? 與一個空串或A、B或C在任何一個字符相匹配 
(Blue|Black)berry 合並常規表達式,與Blueberry或Blackberry相匹配 

2.awk算術運算符 
運算符 用途 
------------------ 
x^y x的y次冪 
x**y 同上 
x%y 計算x/y的余數(求模) 
x+y x加y 
x-y x減y 
x*y x乘y 
x/y x除y 
-y 負y(y的開關符號);也稱一目減 
++y y加1后使用y(前置加) 
y++ 使用y值后加1(后綴加) 
--y y減1后使用y(前置減) 
y-- 使用后y減1(后綴減) 
x=y 將y的值賦給x 
x+=y 將x+y的值賦給x 
x-=y 將x-y的值賦給x 
x*=y 將x*y的值賦給x 
x/=y 將x/y的值賦給x x%=y 將x%y的值賦給x 
x^=y 將x^y的值賦給x 
x**=y 將x**y的值賦給x 

3.awk允許的測試: 
操作符 含義 
x==y x等於y 
x!=y x不等於y 
x>y x大於y 
x>=y x大於或等於y 
x<y x小於y 
x<=y x小於或等於y? 
x~re x匹配正則表達式re? 
x!~re x不匹配正則表達式re? 

4.awk的操作符(按優先級升序排列) 
= 、+=、 -=、 *= 、/= 、 %= 
|| 
&& 
> >= < <= == != ~ !~ 
xy (字符串連結,'x''y'變成"xy") 
+ - 
* / % 
++ -- 

5.awk內置變量(預定義變量) 

說明:表中v項表示第一個支持變量的工具(下同):A=awk,N=nawk,P=POSIX awk,G=gawk 
V 變量 含義 缺省值 
-------------------------------------------------------- 
N ARGC 命令行參數個數 
G ARGIND 當前被處理文件的ARGV標志符 
N ARGV 命令行參數數組 
G CONVFMT 數字轉換格式 %.6g 
P ENVIRON UNIX環境變量 
N ERRNO UNIX系統錯誤消息 
G FIELDWIDTHS 輸入字段寬度的空白分隔字符串 
A FILENAME 當前輸入文件的名字 
P FNR 當前記錄數 
A FS 輸入字段分隔符 空格 
G IGNORECASE 控制大小寫敏感0(大小寫敏感) 
A NF 當前記錄中的字段個數 
A NR 已經讀出的記錄數 
A OFMT 數字的輸出格式 %.6g 
A OFS 輸出字段分隔符 空格 
A ORS 輸出的記錄分隔符 新行 
A RS 輸入的記錄他隔符 新行 
N RSTART 被匹配函數匹配的字符串首 
N RLENGTH 被匹配函數匹配的字符串長度 
N SUBSEP 下標分隔符 "\034" 

6.awk的內置函數 
V 函數 用途或返回值 
------------------------------------------------ 
N gsub(reg,string,target) 每次常規表達式reg匹配時替換target中的string 
N index(search,string) 返回string中search串的位置 
A length(string) 求串string中的字符個數 
N match(string,reg) 返回常規表達式reg匹配的string中的位置 
N printf(format,variable) 格式化輸出,按format提供的格式輸出變量variable。 
N split(string,store,delim) 根據分界符delim,分解string為store的數組元素 
N sprintf(format,variable) 返回一個包含基於format的格式化數據,variables是要放到串中的數據 
G strftime(format,timestamp) 返回一個基於format的日期或者時間串,timestmp是systime()函數返回的時間 
N sub(reg,string,target) 第一次當常規表達式reg匹配,替換target串中的字符串 
A substr(string,position,len) 返回一個以position開始len個字符的子串 
P totower(string) 返回string中對應的小寫字符 
P toupper(string) 返回string中對應的大寫字符 
A atan(x,y) x的余切(弧度) 
N cos(x) x的余弦(弧度) 
A exp(x) e的x冪 
A int(x) x的整數部分 
A log(x) x的自然對數值 
N rand() 0-1之間的隨機數 
N sin(x) x的正弦(弧度) 
A sqrt(x) x的平方根 
A srand(x) 初始化隨機數發生器。如果忽略x,則使用system() 
G system() 返回自1970年1月1日以來經過的時間(按秒計算)





注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com