awk命令:
Linux文本處理三劍客:
grep, egrep, fgrep: 文本過濾器
sed: 行編輯器
模式空間,保持空間
awk: 報表生成器,格式化文本輸出
AWK:a.k.a Aho, Weinberger, Kernighan
將文件的一行讀入,並將其分隔爲多段,用$1..位置變量表示,$0表示全部段
GNU awk = gawk
基本用法:
gawk [options] 'program' file file ...
program: PATTERN { ACTION STATEMENT }
PATTERN省略表示對整個文件的每行文本執行後面的操作,由語句組成,語句分隔符是;
ACTION: print, printf
選項:
-F[]:指明輸入字段分隔符;
-v var_name=value: 變量賦值
例: gawk -F: '{print $1,$3}' /etc/passwd
1、awk的輸出命令print
print item1, item2, ...
要點:
(1) 各item間使用逗號分隔,而輸出時則使用輸出分隔符分隔;
(2) 輸出的各item可以字符串或數值、當前記錄的字段($n)、變量或awk的表達式;數值會被隱式轉換爲字符進行輸出;
(3) print後面的item如果省略,相當於print $0; 輸出“空白”,使用print "";
2、變量
2.1 內置變量
FS: input field seperator,輸入字段分隔符;默認爲空白字符;
RS:input record separator, 輸入行分隔符;默認爲換行符;
例:awk -v RS=[" "/] '{pirint $0}' /etc/passwd
表示輸出/etc/passwd文件,換行符爲空白或/
OFS: output field seperator,輸出字段分隔符,默認爲空白字符;默認爲空白字符;
ORS:output record separator,輸出行分隔符;默認爲換行符;
例:把用戶名輸出爲一行
NF: number of field in current record,當前行的字段數;
NR:行數,所有文件統一計數;
FNR:行數,各文件分別計數;
FILENAME:當前文件名;
ARGC:命令行參數的個數;
例:awk '{print ARGC}' /etc/passwd
>2
awk '{print ARGC}' /etc/passwd /etc/issue
>3
ARGV:數組,保存了命令行參數;
例:
[root@MYPC ~]# awk '{print ARGV[0]}' /etc/issue
awk
[root@MYPC ~]# awk '{print ARGV[1]}' /etc/issue
/etc/issue
2.2 自定義變量
定義變量的位置:
(1) 可以program中定義變量;
(2) 通過-v選項定義變量;
變量名區分字符大小寫
3、printf命令
格式: printf format, item1, item2, ...
要點:
(1) format是必須的;
(2) 不會自動換行,需顯式給定行分隔符\n;
(3) format中需要分別爲後面的每個item指定一個格式符;
格式符:都以%開頭,後跟一個字符
%c: 顯示字符的ASCII碼;
%d,%i: 顯示十進制整數;
%e, %E: 科學計數法顯示數值;
%f: 顯示爲浮點數;
%g, %G: 以科學計數法格式或浮點數格式顯示數值;
%s: 字符串
%u: 無符號的整數
%%: 顯示%自身
修飾符:
#[.#]: 第一個#指定顯示寬度,例如%30s表示用30個字符長度去顯示後面的字符串;第二個#表示小數點後的精度;
-: 左對齊
+:顯示數值正負符號
4、操作符:
算術操作符:
x+y, x-y, x*y, x/y, x^y, x%y
-x: 負值
+x: 轉換爲數值
字符串操作:字符串連接
賦值操作符:
=, +=, -=, *=, /=, %=, ^=
++, --
比較操作符:
>, >=, <. <=, ==, !=
模式匹配符:
~
!~
邏輯操作符:
&&
||
條件表達式:
selector?if-true-expression:if-false-expression
例:# awk -F: '{$3>=500?usertype="common user":usertype="sysuser or admin";printf "%20s:%-s\n",$1,usertype}' /etc/passwd
函數調用:
function_name(argu1,argu2,...)
5、PATTERN
(1) /regular expression/: 僅處理能夠被/regular expression/所匹配到的行;
(2) relational expression:關係表達式,有真假之分,一般來說,其結果爲非0或非空字符串時爲“真”,否則,爲“假”;
表示第6個字段符合後面的模式就輸出這一行
(3) line ranges:行範圍,類似sed或vim的地址定界法;startline, endline,startline和endline使用的是模式表達式
(4) BEGIN/END: 特殊模式
僅在awk運行程序之前執行一次(BEGIN)或僅在awk運行程序之後執行一次(END);
如果只是顯示/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操作。
(5) empty: 空模式,匹配任意行;
6、常用的action
(1) Expressions 表達式
(2) Control statements 控制語句,如if
(3) Compound statements 複合語句
(4) input statements 輸入語句
(5) output statements 輸出語句
7、控制語句
if (condition) statement [ else statement ]
while (condition) statement
do statement while (condition)
for (expr1; expr2; expr3) statement
for (var in array) statement
break
continue
delete array[index]
delete array
exit [ expression ]
{ statements }
7.1 if-else
語法:if (condition) statement [ else statement ]
if (condition) { statements; } [ else { statements; }]
# awk -F: '{if ($3>=500) {print $1," is a common user.";} }' /etc/passwd
# awk -F: '{if ($3>=500) {print $1," is a common user.";} else {print $1," is a system user or admin.";}}' /etc/passwd
# awk '{if (NF>6) print NF, $0 }' /etc/inittab
用法:對awk取得的整行或行中的字段做條件判斷;
7.2 while循環
語法:while (condition) statement
while (condition) { statements }
條件爲真時進行循環,直到爲假退出;
用法:通常用於在當前行的各字段間進行循環;
# awk '{i=1;while(i<=NF){printf "%20s:%d\n",$i,length($i); i++}}' /etc/inittab
# awk '{i=1;while(i<=NF){if(length($i)>4){print $i,length($i)}i++;}}' /etc/inittab length是個內置獲取字符串長度的函數
7.3 do-while循環
語法:do statement while (condition)
do { do-while-body } while (condition)
意義:至少執行一次循環體;
7.4 for循環
語法:for (expr1; expr2; expr3) statement
for (expr1; expr2; expr3) { statements }
for (varaiable assignment; condition; iteration process) { for-body }
# awk '{for(i=1;i<=NF;i++) {printf "%s:%d\n", $i, length($i)}}' /etc/inittab
for循環在awk中有一個專用於遍歷數組元素:
語法:for (var in array) { for-body }
{for(i in array){print aarray[$i]}}'
7.5 switch
語法:switch (expression) {case VALUE or /REGEXP/: statement; ...; default: statementN}
7.6 break and continue
break [n]: 退出當前循環
continue:提前結束本輪循環,直接進入下輪循環
7.7 next
提前結束對本行的處理而進入下一行的處理
取出UID爲偶數的用戶名和UID
#awk -F:'{if($3%2==0){print $1,$3;}}' /etc/passwd
# awk -F: '{if($3%2!=0) next;print $1,$3}' /etc/passwd
8、Array
關聯數組:array[index-expression]
index-expression:
可以使用任意字符串;
如果某數組元素事先不存在,在引用時,awk會自動創建此元素並將其值初始化爲空串;
因此,若要判斷數組是否存在某元素,要使用“index in array”進行;
a[mon]="Monday"
print a[mon]
要遍歷數組中的每個元素,使用: for (var in array) { for body }
注意:var會遍歷array的每一個索引,print array[var]
例:統計每一行中各單詞分別出現的次數
~]# awk '{for(i=1;i<=NF;i++) {count[$i]++}}END{for(j in count) {print j,count[j]}}' awk.txt
~]# ss -tan | awk '!/^State/{state[$1]++}END{for (i in state) {print i,state[i]}}'
~]# netstat -tan | awk '/^tcp/{state[$NF]++}END{for(i in state){print i,state[i]}}'
練習:統計httpd訪問日誌中,每個IP出現的次數;
~]# awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log
9、函數
9.1 內置函數
數值處理:
rand(): 返回0和1之間一個隨機數;
字符串處理:
length([s]): 返回指定字符串的長度
sub(r, s [, t]):以r所表示的模式來查找t字符串中的匹配,將其第一次出現替換同s所表示的字符串;
sub(ab,AB,$0)
gsub(r, s [, t]):以r所表示的模式來查找t字符串中的匹配,將其所有的出現均替換同s所表示的字符串;
split(s, a [, r]): 以r爲分隔符切割字符串s,並將切割的結果保存至a表示數組中;返回字段的個數
~]# netstat -tan | awk '/^tcp/{len=split($5,client,":");ip[client[len-1]]++}END{for(i in ip){print i,ip[i]}}'
substr(s, i [, n]): 從s表示的字符串中取子串,從i開始,取n個字符;
時間類的函數:
systime(): 取時間戳;
位運算函數:
and(v1,va2):
9.2 自定義函數
function f_name(p,q)
{
...
}