awk的工作原理
一次讀取一行文本,按輸入分隔符進行切片,切成多個組成部分,將每片直接保存在內建的變量中,$1,$2,$3....,引用指定的變量,可以顯示指定斷,或者多個斷。如果需要顯示全部的,需要使用$0來引用。可以對單個片斷進行判斷,也可以對所有斷進行循環判斷。其默認分隔符爲空格
awk的基本用法格式
awk [options] 'program' FILE......
語句之間用分號分隔
[options]
-F : 指明輸入時用到的字段分隔符
-v var=VALUE : 自定義變量
在awk中變量的引用不需要加$,而是直接引用
awk用法的簡要介紹
第一種模式
awk [options] 'scripts' file1,file2.....
在這種模式中,scripts主要是命令的堆砌,對輸入的文本行進行處理,通過命令print,printf或是輸出重定向的方式顯示出來,這裏經常用到的知識點是:awk的內置變量,以及命令print和printf的使用
第二種模式
awk [options] 'PATTERN{action}' file,file2.....
在這種模式中,最重要的是5種模式和5種action的使用,以及awk的數組的使用和內置函數
第一種模式
1、print
1、各項目之間使用逗號隔開,而輸出時則以空白字符分隔
2、輸出的Item可以爲字符串或數值,當前記錄的字段(如$1)、變量或awk的表達式,數值會先轉換爲字符串,而後再輸出
3、print命令後面的Item可以省略,此時其功能相當於print $0,因此,如果想輸出空白行,則需要使用print""
4、如果引用變量$1或其他的,是不能使用引號引起來
2、內置變量
FS : input field seperator,輸入的分隔符,默認爲空白字符
OFS: output field seperator,輸出的分隔符,默認爲空白字符
RS : input record seperator,輸入的換行符
ORS: output record seperator,輸出時的換行符
NF : number of field ,字段個數
awk '{print NF}' /etc/fstab :打印每行的最後一個字段爲第幾個字段,這裏是數量引用,不是對應的值引用
awk '{print $NF}' /etc/fstab : 打印每行中的最後一個字段
NR : number of record,文件中的行數
awk '{print NR}' /etc/fstab: 打印行號,其會個行號都顯示
awk 'END{print NR}' /etc/fstab: 顯示文本的總行數,其只是在文本處理完成後,只顯示一次行號
awk '{print NR}' file1 file2 : 會每把所有文檔進行總的編號,而不是單獨對文件進行編號
FNR : 對每個文件進行行數單獨編號
awk '{print FNR}' file1 file2 : 會對每個文件的行數進行單獨的編號顯示
FILENAME : awk命令所處理的文件的名稱
awk '{print FILENAME}' file1 : 顯示當前文件名,但會每行顯示一次
awk 'END{print FILENAME}' file1 : 顯示當前文件名,但只會顯示一次
ARGC : 命令行中參數的個數,其awk命令也算一個參數
awk 'END{print ARGC}' /etc/fstab : 顯示共有幾個參數
ARGV : 其是一個數組,保存的是命令行所給定的各參數
awk 'END{print ARGV[0]}' /etc/fstab : 顯示第一個參數,默認第一個參數個awk命令本身
3、自定義變量
-v var=VALUE : 在選項位置定義
awk 'BEGIN{test="hello";print test}' : 在program中定義
變量在program中定義時,需要使用引號引起來
4、printf命令
其格式化輸出:printf FORMAT,item1,item2....
要點:
1、其與print命令最大不同是,printf需要指定format
2、printf後面的字串定義內容需要使用雙引號引起來
3、字串定義後的內容需要使用","分隔,後面直接跟Item1,item2....
4、format用於指定後面的每個item的輸出格式
5、printf語句不會自動打印換行符,\n
格式符
%c: 顯示字符的ASCII碼
%d,%i : 顯示十進制整數
%e,%E: 科學計數法數值顯示
%f : 顯示爲浮點數
%g,%G: 以科學數法或浮點形式顯示數值
%s: 顯示字符串
%u: 無符號整數
%%: 顯示%號自身,相當於轉義
修飾符
N : 顯示寬度
- : 左對齊(默認爲右對齊)
+ : 顯示數值符號
示例:
awk -F: '{printf "%s\n",$1}' /etc/fstab
awk -F: '{printf "username: %s,UID:%d\n",$1,$3}' /etc/passwd
awk -F: '{printf "username: %-20s shell: %s\n",$1,$NF}' /etc/passwd
輸出重定向
print items > "output-file"
print items >> "output-file"
print items | command
特殊文件描述符:
/dev/stdin :標準輸入
/dev/stdout:標準輸出
/dev/stderr:錯誤輸出
/dev/fd/N : 某特定文件描述符,如/dev/stdin就相當於/dev/fd/0
* 示例
* awk -F: '{printf "%-15s %i\n",$1,$3 > "/dev/stderr"}' /etc/passwd
第二種模式
awk [option] 'PATTERN{action}' file1,file2....
PATTERN的使用
REGEXP:正則表達式,格式爲/regular expression/,僅處理能夠被此處模式匹配到的行
awk '/^UUID/{print $1}' /etc/fstab
awk '!/^UUID/{print $1}' /etc/fstab
relational expression:表達式,其值非0或爲非空字符時滿足條件,用運算符~(匹配)和!~(不匹配)
$1 ~ /foo/ 或者 $1 == "magedu"
Ranges : 指定匹配範圍,格式爲/pat1/,/pat2/
awk -F: '{NR>=2&&<=10){print $1}' /etc/passwd
awk -F: /^root/,/^myuser/{print $1}' /etc/passwd
注意:不支持直接給出數字的格式
BEGIN/END模式 : 特殊模式,僅在awk命令執行前運行一次或結束前運行一次
awk -F: 'BEGIN{print "Username ID Shell"}{printf "%-10s%-10s%-20s\n",$1,$3,$7}' /etc/passwd :先打印一個表頭
awk -F: 'BEGIN{print "username ID Shell"}{printf "%-10s%-10s%-20s\n",$1,$3,$7}END{print "end of report."} /etc/passwd :打印一個表尾
Empty(空模式):匹配任意輸入行
/正則表達式/:使用通配符的擴展集。
關係表達式:可以用下面運算符表中的關係運算符進行操作,可以是字符串或數字的比較,如$2>$1選擇第二個字段比第一個字段長的行。
模式匹配表達式:
模式,模式:指定一個行的範圍。該語法不能包括BEGIN和END模式。
BEGIN:讓用戶指定在第一條輸入記錄被處理之前所發生的動作,通常可在這裏設置全局變量。
END:讓用戶在最後一條輸入記錄被讀取之後發生的動作。
常見的Action
1)Expressions
2) Control statements :if while等
3) Compound statements:組合語句
4) Input statements
5) Output statements
控制語句
1、if-else
語法:if (condition){then-body} else{[else-body]}
示例:
awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd
awk -F: '{if($3>=1000){printf "Common user: %s\n",$1} else {printf "root or sysuser: %s\n",$1}}' /etc/passwd
awk -F: '{if($NF=="/bin/bash")print $1}' /etc/passwd
awk -F: '{if(NF>5) print $0}' /etc/fstab
df -h | awk -F[%] '/^\/dev/{print $1}' | awk {if($NF>=20) print $1}'
awk -F: '{if($1=="root") print $1,"Admin";else print $1, "Common User"}' /etc/passwd
awk -F: '{if($1=="root") printf "%-15s: %s\n",$1,"Admin";else printf "%-15s: %s\n",$1, "Common user"}' /etc/passwd
awk -F: -v sum=0 '{if($3>=500) sum++}END{print sum}' /etc/passwd : 統計用戶ID大於500的有多少行
awk -F: -v OFS="\t" '{if($3<=999)printf "Sys user:\t%-15s ID is :%d\n", $1,$3;else{printf "Common user:\t%-15s ID is :%d\n",$1,$3}}' /etc/passwd :可以使用\t製表符控制 輸出格式
2、while:用於循環字段的
語法:while (condition){statement1;statment2;....}
示例:
awk '/^[[:space:]]*linux16/{print}' /boot/grub2/grub.cfg
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i,length($i);i++}}' /etc/grub2.cfg :對每個字段進行字符個數統計
awk '/^[[:space:]]]*linux16/{i=1;while(i<=NF){if(length($i)<=7)print $i,length($i);i++}}' /etc/grub2.cfg
awk -F: '{i=1;while(i<=3){print $i;i++}}' /etc/passwd:打印用戶名、密碼佔位符、ID
awk -F: '{i=1;while(i<=NF){if(length($i)>=4){print $i};i++}}' /etc/passwd : 字段大小於等於4的都顯示
3、do-while
語法:do {statement1,statement2,....} while (dondition)
示例:
awk -F: '{i=1;do{print $i;i++}while(i<=3)}' /etc/passwd :打印用戶名、密碼佔位符、UID
4、for
語法:for(variable assignment;condition;iteration process){ statement1,statement2,...}
示例:
awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
awk -F: '{for(i=1;i<=3;i++)print $i}' /etc/passwd
awk -F: '{for(i=1;i<=NF;i++) { if (length($i)>=4) {print $i}}}' /etc/passwd5、for循環還可以用來遍歷數組元素
語法:for (i in array) {statement1,statement2,....}
示例
awk -F: '$NF!~/^$/{BASH[$NF]++}END{for(A in BASH){printf "%15s:%i\n",A,BASH[A]}}' /etc/passwd
awk ‘{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count) {print i,count[i]}}’ /etc/fstab
awk '/^UUID/{filesystem[$3]++}END{for (i in filesystem) {print i,filesystem[i]}}' /etc/fstab :統計/etc/fstab中各文件系統的次數
netstat -tan | awk '/^tcp>/{state[$NF]++}END{for(i in state) {print i,state[i]}}' : 統計各連接狀態的次數
awk '{ip[$1]++} END {for (i in ip) {print i,ip[i]}}' /var/log/httpd/access_log : 統計訪問日誌中各IP的訪問次數
6、case
語法:switch (expression) { case VALUE or /REGEXP/: statement1, statement2,... default: statement1, ...}
7、break和continue
break [n]
continue : 進入下一個字段
8、next
功能:提前結束本行文本的處理,並接着處理下一行
示例:
awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd
awk -F: ‘{if ($3%2!=0) next;print $1,$3}’ /etc/passwd
awk的操作符
1、算術操作符
-x : 負值
+x : 轉換爲數值
x^y :
x**y : 次方
x*y : 乘法
x/y : 除法
x+y :
x-y :
x%y :
2、字符串操作符
只有一個,而且不用寫出來,用於實現字符串連接
3、賦值操作符
=
+=
-=
*=
/=
%=
^=
**=
++
--
需要注意的是,如果某模式爲=號,此時使用/=/可能會有語法錯誤,就以/[=]/替代
4、布爾值
awk中,任何非0值或非空字符串都爲真,反之爲假
5、比較操作符
x<y
x<=y
x>y
x>=y
x==y
x!=y
x~y
x!~y
6、邏輯操作符
&&
||
!
7、函數調用
`func_name(argu1,argu2,....)
8、條件表達式
selector?if-true-expression:if-false-expression
awk -F: ‘{$3>=1000?usertype=“Common user”:usertype=“Sysadmin or sysUser”;printf “%15s:%-s\n",$1,usertype}’ /etc/passwd
awk的數組
1、定義數組的注意事項
1、可使用任意字符串,字符串要使用雙引號
2、如果某數組元素事先不存在,在引用時awk會自動創建此元素,並將其初始化爲空串
3、要遍歷數組中的每個元素,要使用for循環
for(var in array){statement1,.....}
注意:var用於引用數組時,是引用的下標,而不是元素值
定義數組的格式
awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print week["mon"]}'
示例:
統計netstat -tan中各狀態的次數
netstat -tan | awk '/^tcp>/{state[$NF]++}END{for(i in state) {print i,state[i]}}’
每出現一被/^tcp/模式匹配到的行,數組S[$NF]就加1,NF爲當前匹配到的行的最後一個字段,此處用其值做爲數組S的元素索引
統計www服務的訪問日誌中IP數量
awk ‘{ip[$1]++} END {for (i in ip) {print i,ip[i]}}’ /var/log/httpd/access_log
統計/etc/fstab文件中每個文件系統類型出現的次數
awk '/^UUID/{filesystem[$3]++}END{for (i in filesystem) {print i,filesystem[i]}}' /etc/fstab
統計指定文件中單詞的出現次數
awk ‘{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count) {print i,count[i]}}’ /etc/fstab
統計出/etc/passwd文件中shell的種類和個數
awk -F: '{shell[$NF]++}END{for(A in shell){print A,shell[A]}}' /etc/passwd
重點解析一下這條命令:
首先,shell[$NF]++這個語句。其中的$NF所代表的是一個字符串,即shell的類型。也就是說在這個數組中的元素名稱是shell的名稱。而後面的++是對這個數組進行賦值。因爲在/etc/passwd 這個文件中的shell類型只有兩種,即/bin/bash和/sbin/nologin。也就是說這個數組就只有兩個元素,並且它的值是不斷被更新的。其次,語句for(A in shell)是設定了A是數組shell中的座標變量,即是A是元素的名稱,shell[A]是數組的值。
刪除數組變量
delete array[index]
awk的內置函數
split(string, array [, fieldsep [, seps ] ])
功能:將string表示的字符串以fieldsep爲分隔符進行分隔,並將分隔後的結果保存至array爲名的數組中;數組下標爲從0開始的序列;
示例:
netstat -tn | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for(i in count)print i,count[i]}'
netstat -ant | awk '/:80\>/{split($5,clients,":");IP[clients[1]]++}END{for(i in IP){print IP[i],i}}' | sort -rn | head -50
length([string])
功能:返回string字串中字符的個數
substr(string, start [, length])
功能:取string字符串中的子串,從start開始,取length個;start從1開始計數
system(command)
功能:執行系統command並將結果返回至awk命令
systime()
功能:取系統當前時間
tolower(s)
功能:將s中的所有字母轉爲小寫
toupper(s)
功能:將s中的所有字母轉爲大寫
番外篇!!!!
Linux Web服務器網站故障分析常用的命令
系統連接狀態篇:
1.查看TCP連接狀態
netstat -nat |awk ‘{print $6}’|sort|uniq -c|sort -rn
netstat -n | awk ‘/^tcp/ {++S[$NF]};END {for(a in S) print a, S[a]}’ 或
netstat -n | awk ‘/^tcp/ {++state[$NF]}; END {for(key in state) print key,"\t",state[key]}’
netstat -n | awk ‘/^tcp/ {++arr[$NF]};END {for(k in arr) print k,"t",arr[k]}’
netstat -n |awk ‘/^tcp/ {print $NF}’|sort|uniq -c|sort -rn
netstat -ant | awk ‘{print $NF}’ | grep -v ‘[a-z]‘ | sort | uniq -c
2.查找請求數請20個IP(常用於查找攻來源):
netstat -anlp|grep 80|grep tcp|awk ‘{print $5}’|awk -F: ‘{print $1}’|sort|uniq -c|sort -nr|head -n20
netstat -ant |awk ‘/:80/{split($5,ip,":");++A[ip[1]]}END{for(i in A) print A[i],i}’ |sort -rn|head -n20
3.用tcpdump嗅探80端口的訪問看看誰最高
tcpdump -i eth0 -tnn dst port 80 -c 1000 | awk -F"." ‘{print $1"."$2"."$3"."$4}’ | sort | uniq -c | sort -nr |head -20
4.查找較多time_wait連接
netstat -n|grep TIME_WAIT|awk ‘{print $5}’|sort|uniq -c|sort -rn|head -n20
5.找查較多的SYN連接
netstat -an | grep SYN | awk ‘{print $5}’ | awk -F: ‘{print $1}’ | sort | uniq -c | sort -nr | more
6.根據端口列進程
netstat -ntlp | grep 80 | awk ‘{print $7}’ | cut -d/ -f1
netstat -tnlp | awk '/22/{split($NF,port,"/");d[port[1]]++}END{for(i in d)print i}'
網站日誌分析篇1(Apache):
1.獲得訪問前10位的ip地址
cat access.log|awk ‘{print $1}’|sort|uniq -c|sort -nr|head -10
cat access.log|awk ‘{counts[$(11)]+=1}; END {for(url in counts) print counts[url], url}’
2.訪問次數最多的文件或頁面,取前20
cat access.log|awk ‘{print $11}’|sort|uniq -c|sort -nr|head -20
3.列出傳輸最大的幾個exe文件(分析下載站的時候常用)
cat access.log |awk ‘($7~/.exe/){print $10 " " $1 " " $4 " " $7}’|sort -nr|head -20
4.列出輸出大於200000byte(約200kb)的exe文件以及對應文件發生次數
cat access.log |awk ‘($10 > 200000 && $7~/.exe/){print $7}’|sort -n|uniq -c|sort -nr|head -100
5.如果日誌最後一列記錄的是頁面文件傳輸時間,則有列出到客戶端最耗時的頁面
cat access.log |awk ‘($7~/.php/){print $NF " " $1 " " $4 " " $7}’|sort -nr|head -100
6.列出最最耗時的頁面(超過60秒的)的以及對應頁面發生次數
cat access.log |awk ‘($NF > 60 && $7~/.php/){print $7}’|sort -n|uniq -c|sort -nr|head -100
7.列出傳輸時間超過 30 秒的文件
cat access.log |awk ‘($NF > 30){print $7}’|sort -n|uniq -c|sort -nr|head -20
8.統計網站流量(G)
cat access.log |awk ‘{sum+=$10} END {print sum/1024/1024/1024}’
9.統計404的連接
awk ‘($9 ~/404/)’ access.log | awk ‘{print $9,$7}’ | sort
10. 統計http status
cat access.log |awk ‘{counts[$(9)]+=1}; END {for(code in counts) print code, counts[code]}'
cat access.log |awk '{print $9}'|sort|uniq -c|sort -rn
10.蜘蛛分析,查看是哪些蜘蛛在抓取內容。
/usr/sbin/tcpdump -i eth0 -l -s 0 -w - dst port 80 | strings | grep -i user-agent | grep -i -E 'bot|crawler|slurp|spider'
網站日分析2(Squid篇)按域統計流量
zcat squid_access.log.tar.gz| awk '{print $10,$7}' |awk 'BEGIN{FS="[ /]"}{trfc[$4]+=$1}END{for(domain in trfc){printf "%st%dn",domain,trfc[domain]}}'
數據庫篇
1.查看數據庫執行的sql
/usr/sbin/tcpdump -i eth0 -s 0 -l -w - dst port 3306 | strings | egrep -i 'SELECT|UPDATE|DELETE|INSERT|SET|COMMIT|ROLLBACK|CREATE|DROP|ALTER|CALL'
系統Debug分析篇
1.調試命令
strace -p pid
2.跟蹤指定進程的PID
gdb -p pid
9.gawk應用實現
(1)內建變量; 1)字段和數據行分隔符變量
數據字段變量:
允許你使用美元符號($)和數據字段在數據行中位置對應的數值來引用該數據行中的字段
字段分隔符:
默認情況下,字段分隔符是一個空白字符,也就是空格符或製表符(tab)。在命令行下使用命令行參數-F或者在gawk程序中使用特殊的內置變量FS來更改字段分隔符
變量彙總:
FIELDWIDTHS 由空格分隔開的定義了每個數據字段確切寬度的一列數字
FS 輸入字段分隔符
RS 輸入數據行分隔符
OFS 輸出字段分隔符
ORS 輸出數據行分隔符
變量FS和OFS定義了gawk如何處理數據流中的數據字段
[root@ahui ~]# cat data1
data11,data12,data13,data14,data15
data21,data22,data23,data24,data25
data31,data32,data33,data34,data35
[root@ahui ~]# awk 'BEGIN{FS=","; OFS="-"} {print $1,$2,$3}' data1
data11-data12-data13
data21-data22-data23
data31-data32-data33
FIELDWIDTHS變量允許你讀取數據行,而不用字段分隔符來劃分字段。一旦設置了FIELDWIDTHS變量,gawk就會忽略FS變量,而根據提供的字段寬度大小來計算字段
[root@ahui ~]# cat data1b
1005.3847887.37
133-2.374893.23
23987.8237488.9
[root@ahui ~]# awk 'BEGIN{FIELDWIDTHS="3 5 2 5"} {print $1,$2,$3,$4}' data1b
100 5.384 78 87.37
133 -2.37 48 93.23
239 87.82 37 488.9
默認情況下,gawk將RS和ORS設爲換行符,但是有時會碰到數據流中字段佔了多行的情況。
經典的例子就是包含地址和電話號碼的數據,其中地址和電話號碼各佔一行
[root@ahui ~]# cat data2
Riley Mullen
123 main street
Chicago, IL 60601
(312)555-2378
Frank Williams
123 main street
Chicago, IL 60601
(312)234-3253
Haley Snell
123 main street
Chicago, IL 60601
(312)123-2374
[root@ahui ~]# gawk 'BEGIN{FS="\n"; RS=""} {print $1,$4}' data2
Riley Mullen (312)555-2378
Frank Williams (312)234-3253
Haley Snell (312)123-2374
只需把FS變量設置成換行符。這就表明數據流中的每行都是一個單獨的字段,每行上的所有數據都屬於同一個字段。
接着只需把RS變量設置成空字符串,然後在數據行間留一個空白行。gawk會把每個空白行當做一個數據行分隔符
2)數據變量:
ARGC 當前命令行參數個數
ARGIND 當前文件在ARGV中的位置
ARGV 包含命令行參數的數組
CONVFMT 數字的轉換格式(參見printf語句);默認值爲%.6 g
ENVIRON 當前shell環境變量及其值組成的關聯數組
ERRNO 當讀取或關閉輸入文件發生錯誤時的系統錯誤號
FILENAME 用作gawk輸入數據的數據文件的文件名
FNR 當前數據文件中的數據行數
IGNORECASE 設成非零值時,忽略gawk命令中出現的字符串的字符大小寫
NF 數據文件中的字段總數
NR 已處理的輸入數據行數目
OFMT 數字的輸出格式;默認值爲%.6 g
RLENGTH 由match函數所匹配的子字符串的長度
RSTART 由match函數所匹配的子字符串的起始位置
相關實例:
[root@ahui ~]# gawk 'BEGIN{print ARGC,ARGV[1]}' data1
2 data1
注意:ARGC變量表明命令行上有兩個參數,這包括gawk命令和data1參數(記住,程序腳本並不算參數)。
ARGV數組從代表該命令的索引0開始,第一個數組值是gawk命令後的第一個命令行參數。
[root@ahui ~]# gawk '
> BEGIN{
> print ENVIRON["HOME"]
> print ENVIRON["PATH"]
> }'
/root
/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
注意:ENVIRON變量使用關聯數組來提取shell環境變量。關聯數組用文本作爲數組的索引值,而不用數值。可用這種方式來從shell中提取任何環境變量的值來在gawk程序中使用。
[root@ahui ~]# gawk 'BEGIN{FS=":"; OFS=":"} {print $1,$NF}' /etc/passwd
root:/bin/bash
bin:/sbin/nologin
daemon:/sbin/nologin
adm:/sbin/nologin
lp:/sbin/nologin
sync:/bin/sync
shutdown:/sbin/shutdown
halt:/sbin/halt
mail:/sbin/nologin
uucp:/sbin/nologin
operator:/sbin/nologin
games:/sbin/nologin
gopher:/sbin/nologin
ftp:/sbin/nologin
nobody:/sbin/nologin
dbus:/sbin/nologin
usbmuxd:/sbin/nologin
vcsa:/sbin/nologin
rpc:/sbin/nologin
rtkit:/sbin/nologin
avahi-autoipd:/sbin/nologin
abrt:/sbin/nologin
rpcuser:/sbin/nologin
nfsnobody:/sbin/nologin
haldaemon:/sbin/nologin
gdm:/sbin/nologin
ntp:/sbin/nologin
apache:/sbin/nologin
saslauth:/sbin/nologin
postfix:/sbin/nologin
pulse:/sbin/nologin
sshd:/sbin/nologin
tcpdump:/sbin/nologin
ahui:/bin/bash
注意:NF變量允許你指定數據行中的最後一個數據字段,即NF變量含有數據文件中最後一個數據字段的數字值。可以在它前面加個美元符將它用作字段變量。
[root@ahui ~]# gawk '
> BEGIN{FS=","}
> {print $1, "FNR="FNR, "NR="NR}
> END{print "There were",NR,"records processed"}' data1 data1
data11 FNR=1 NR=1
data21 FNR=2 NR=2
data31 FNR=3 NR=3
data11 FNR=1 NR=4
data21 FNR=2 NR=5
data31 FNR=3 NR=6
There were 6 records processed
注意:FNR變量的值在gawk處理第二個文件時被重置了,而NR變量則在進入第二個數據文件後繼續計數
注意變量賦值的語法結構:"FNR="FNR。引號位置錯了會出現語法錯誤
(2)自定義變量 1)在腳本中給變量賦值
[root@ahui ~]# gawk '
> BEGIN{
> testing="This is a test"
> print testing
> }'
This is a test
注意:gawk編程語言包含了用來處理數字值的標準數學操作符,其中包括求餘符號(%)和冪運算符號(^或**)
2)在命令行上給變量賦值
[root@ahui ~]# cat script1
BEGIN{FS=","}
{print $n}
[root@ahui ~]# gawk -f script1 n=2 data1
data12
data22
data32
這個特性允許你改變腳本的行爲而不需要修改實際的腳本代碼。
使用命令行參數來定義變量值會有個問題。在你設置了變量後,這個值在代碼的BEGIN部分不可用
[root@ahui ~]# cat script2
BEGIN{print "The starting value is",n; FS=","}
{print $n}
[root@ahui ~]# gawk -f script2 n=3 data1
The starting value is
data13
data23
data33
可以用-v命令行參數來解決這個問題。它允許你指定在BEGIN代碼部分之前設定的變量。在命令行上,-v命令行參數必須放在腳本代碼之前
[root@ahui ~]# gawk -v n=3 -f script2 data1
The starting value is 3
data13
data23
data33
(3)處理數組
gawk編程語言使用關聯數組來提供數組功能,關聯數組跟數字數組不同之處在於它的索引值可以是任意文本字符串。每個索引字符串都必須是唯一的,並唯一地標識賦給它的數據元素。
1)定義數組變量
數組變量賦值的格式如下;
var[index] = element
其中var是變量名,index是關聯數組的索引值,element是數據元素值。
[root@ahui ~]# gawk 'BEGIN{
> capital["Illinois"] = "Springfiled"
> print capital["Illinois"]
> }'
Springfiled
2)遍歷數組變量
可以用for語句的一種特殊形式:
for (var in array)
{
statements
}
這個for語句會在每次將關聯數組array的下一個索引值賦給變量var時,執行一遍statements。重要的是記住這個變量是索引值而不是數據元素值。
[root@ahui ~]# gawk 'BEGIN{
> var["a"] = 1
> var["b"] = 2
> var["c"] = 3
> var["d"] = 4
> for (test in var)
> {
> print "Index:",test," - value:",var[test]
> }
> }'
Index: a - value: 1
Index: b - value: 2
Index: c - value: 3
Index: d - value: 4
3)刪除數組變量
從關聯數組中刪除數組索引要用一個特別的命令:
delete array[index]
刪除命令會從數組中刪除關聯索引值和相關的數據元素值
[root@ahui ~]# gawk 'BEGIN{
> var["a"] = 1
> var["g"] = 2
> for (test in var)
> {
> print "Index:",test," - value:",var[test]
> }
> delete var["g"]
> print "---"
> for (test in var)
> print "Index:",test," - Value:",var[test]
> }'
Index: a - value: 1
Index: g - value: 2
---
Index: a - Value: 1
(4)使用模式
1)正則表達式
在使用正則表達式時,正則表達式必須出現在它要控制的程序腳本的左花括號前:
[root@ahui ~]# gawk 'BEGIN{FS=","} /11/{print $1}' data1
data11
2)匹配操作符
匹配操作符(matching operate)允許將正則表達式限定在數據行中的特定數據字段。匹配操作符是波浪線(~)。
$1 ~ /^data/
$1變量代表數據行中的第一個數據字段,這個表達式會過濾出第一個字段以文本data開頭的所有數據行。
[root@ahui ~]# gawk 'BEGIN{FS=","} $2 ~ /^data2/{print $0}' data1
data21,data22,data23,data24,data25
也可以使用!符號來排除正則表達式的匹配:
$1 !~ /expression/
[root@ahui ~]# gawk '$1 !~ /root/{print $1,$NF}' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin
gopher:x:13:30:gopher:/var/gopher:/sbin/nologin gopher:x:13:30:gopher:/var/gopher:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin nobody:x:99:99:Nobody:/:/sbin/nologin
dbus:x:81:81:System bus:/:/sbin/nologin
usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin
vcsa:x:69:69:virtual owner:/dev:/sbin/nologin
rpc:x:32:32:Rpcbind Daemon:/var/cache/rpcbind:/sbin/nologin
rtkit:x:499:497:RealtimeKit:/proc:/sbin/nologin rtkit:x:499:497:RealtimeKit:/proc:/sbin/nologin
avahi-autoipd:x:170:170:Avahi Stack:/var/lib/avahi-autoipd:/sbin/nologin
abrt:x:173:173::/etc/abrt:/sbin/nologin abrt:x:173:173::/etc/abrt:/sbin/nologin
rpcuser:x:29:29:RPC User:/var/lib/nfs:/sbin/nologin
nfsnobody:x:65534:65534:Anonymous User:/var/lib/nfs:/sbin/nologin
haldaemon:x:68:68:HAL daemon:/:/sbin/nologin
gdm:x:42:42::/var/lib/gdm:/sbin/nologin gdm:x:42:42::/var/lib/gdm:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin ntp:x:38:38::/etc/ntp:/sbin/nologin
apache:x:48:48:Apache:/var/www:/sbin/nologin apache:x:48:48:Apache:/var/www:/sbin/nologin
saslauth:x:498:76:Saslauthd user:/var/empty/saslauth:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin
pulse:x:497:496:PulseAudio Daemon:/var/run/pulse:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin tcpdump:x:72:72::/:/sbin/nologin
ahui:x:500:500:ahuihaerbin:/home/ahui:/bin/bash ahui:x:500:500:ahuihaerbin:/home/ahui:/bin/bash
3)數學表達式
可以使用任意的普通股數學比較表達式
x == y:值x等於y
x <= y:
x < y
x >= y
x > y
可以對文本數據使用表達式,但是它跟正則表達式不同,表達式必須完全匹配。
[root@ahui ~]# gawk -F, '$1 == "data"{print $1}' data1
[root@ahui ~]# gawk -F, '$1 == "data11"{print $1}' data1
data11
(5)結構化命令 1)if語句 格式: if (condition) statement1 或 if (condition) statement1
[root@ahui ~]# cat data4
10
5
13
50
34
[root@ahui ~]# gawk '{
> if ($1 > 20)
> {
> x = $1 * 2
> print x
> }
> }' data4
100
68
gawk的if語句也支持else子句,允許在if語句條件不成立的情況下執行一條或多條語句
[root@ahui ~]# gawk '{
> if ($1 > 20)
> {
> x = $1 * 2
> print x
> } else
> {
> x = $1 / 2
> print x
> }}' data4
5
2.5
6.5
100
68
也可以在單行上使用else子句,但必須在if語句部分使用分號:
if (condition) statement1; else statement2
[root@ahui ~]# gawk '{if ($1 > 20) print $1 * 2; else print $1 / 2}' data4
5
2.5
6.5
100
68
2)while語句
while語句的格式:
while (condition)
{
statements
}
while循環允許遍歷一組數據,並檢查結束迭代的條件
[root@ahui ~]# gawk '{
total = 0
i = 1
while (i < 4)
{
total += $i
i++
}
avg = total / 3
print "Average:" ,avg
}' data5
Average: 128.333
Average: 137.667
Average: 176.667
[root@ahui ~]# gawk '{
> total = 0
> i = 1
> while (i < 4)
> {
> total += $i
> if (i == 2)
> break
> i++
> }
> avg = total / 2
> print "The average of the first two data elements is:",avg
> }' data5
The average of the first two data elements is: 125
The average of the first two data elements is: 136.5
The average of the first two data elements is: 157.5
3)do-while語句
do-while語句類似於while語句,但會在檢查條件語句之前執行命令。格式爲:
do
{
statement
} while (condition)
這種格式保證了語句會在條件被評估之前至少執行一次
4)for語句
gawk支持C風格的for循環:
for( variable assignment; condition; iteration process)
(6)格式化打印 注意到print語句在gawk如何顯示數據上並未提供多少控制。你能做的大概只是控制輸出字段分隔符(OFS)。
因此要用到格式化打印,我們需要printf命令。格式:
printf "format string", var1, var2...
format string是格式化輸出地關鍵。它會用到文本元素和格式化指定符來具體指定如何呈現格式化輸出。
格式化指定符采用如下格式:
%[modifier]control-letter
其中control-letter是指明顯示什麼類型數據值的單字符碼,而modifier定義了另一個可選的格式化特性。
c 將一個數作爲ASCII字符顯示
d 顯示一個整數值
i 顯示一個整數值(跟d一樣)
e 用科學計數法顯示一個數
f 顯示一個浮點數
g 用科學計數法或浮點數中較短的顯示
o 顯示一個八進制值
s 顯示一個文本字符串
x 顯示一個十六進制值
X 顯示一個十六進制值,但用大寫字母A-F
除了控制字母外,還有3種修飾符可以用來進一步控制輸出
width:
指定了輸出字段最小寬度的數字值。如果輸出短於這個值,printf會向右對齊,並用空格來填充這段空間。如果輸出比指定的寬度還要長,它就會覆蓋width值。
prec:
指定了浮點數中小數點後面位數的數字值,或者文本字符串中顯示的最大字符數。
-(減號):
減號指明在向格式化空間中放入數據時採用左對齊而不是右對齊
[root@ahui ~]# gawk 'BEGIN{FS="\n"; RS=""} {printf "%s %s\n", $1, $4}' data2
Riley Mullen (312)555-2378
Frank Williams (312)234-3253
Haley Snell (312)123-2374
注意:我們需要自己手動在printf命令的末尾添加換行符來生成新行。沒加的話,printf命令會繼續用同一行來打印後續輸出。
如果你需要用幾個單獨的printf命令來在同一行上打印多個輸出,它會非常有用:
[root@ahui ~]# gawk 'BEGIN{FS=","} {printf "%s ", $1} END{printf "\n"}' data1
data11 data21 data31
接着我們用修飾符來格式化第一個字符串值:
[root@ahui ~]# gawk 'BEGIN{FS="\n"; RS=""} {printf "%16s %s\n",$1, $4}' data2
Riley Mullen (312)555-2378
Frank Williams (312)234-3253
Haley Snell (312)123-2374
通過添加一個值爲16的修飾符,我們強制第一個字符串的輸出採用16位字符。默認情況下,printf命令使用右對齊來將數據放到格式化空間中。要改成左對齊,只要給修飾符加一個減號就可以了。
[root@ahui ~]# gawk 'BEGIN{FS="\n"; RS=""} {printf "%-16s %s\n", $1, $4}' data2
Riley Mullen (312)555-2378
Frank Williams (312)234-3253
Haley Snell (312)123-2374
(7)自定義函數 1)定義函數 要定義自己的函數,你必須使用function關鍵字: function name([variables]) { statements }
函數名必須能夠唯一標識函數
2)使用自定義函數
在定義函數時,它必須出現在所有代碼塊之前(包括BEGIN代碼塊)。
[root@ahui ~]# gawk '
> function myprint()
> {
> printf "%-16s - %s\n", $1, $4
> }
> BEGIN{FS="\n"; RS=""}
> {
> myprint()
> }' data2
Riley Mullen - (312)555-2378
Frank Williams - (312)234-3253
Haley Snell - (312)123-2374
3)創建函數庫
首先創建一個存儲所有gawk函數的文件
[root@ahui ~]# cat funclib
function myprint()
{
printf "%-16s - %s\n", $1, $4
}
function myrand()
{
return int(limit * rand())
}
function printthird()
{
printf $3
}
注意:不能將-f命令行參數和內聯gawk腳本放到一起使用,不過可以在同一個命令行中使用多個-f參數
[root@ahui ~]# cat script4
BEGIN{ FS="\n"; RS="" }
{
myprint()
}
[root@ahui ~]# gawk -f funclib -f script4 data2
Riley Mullen - (312)555-2378
Frank Williams - (312)234-3253
Haley Snell - (312)123-2374