本文詳細介紹了awk語法結構、關鍵字、運算符、內置變量、數組、正則表達式元字符、內置函數、算術函數、條件語句和循環等方面的內容,並經過豐富的示例來幫助你們熟悉使用awk。
上篇文章回顧: Linux IPsec離奇事件
awk是一個強大的文本分析工具。正則表達式
awk其名稱得自於它的創始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首個字母。實際上 awk 的確擁有本身的語言: awk 程序設計語言 , 三位建立者已將它正式定義爲「樣式掃描和處理語言」。它容許您建立簡短的程序,這些程序讀取輸入文件、爲數據排序、處理數據、對輸入執行計算以及生成報表,還有無數其餘的功能。數組
awk option scrips filebash
option運維
-F 指定分隔符,而不以默認的空白(空格符或者製表符)函數
-v 指定變量工具
-f 指定腳本的文件名,即將腳本放入一個文件當中,避免 awk 書寫太長,影響視覺,和重複編寫測試
BEGIN 關鍵字會執行一次指定的腳本段
優化
END 關鍵字定義的腳本段,在全部操做完成以後ui
處理數據的腳本段在第二個程序中定義spa
代碼裏井號開頭部分的表明輸出內容(輸出不包含井號),下同
cat mi_info
# mi6-1 5.15" 835 6G 64G 3350mAh 2499
# mi6-2 5.15" 835 6G 128G 3350mAh 2899
# minote3-1 5.5" 660 6G 64G 3500mAh 2499
# minote3-2 5.5" 660 6G 128G 3500mAh 2899
# mix2-1 5.99" 835 6G 64G 3400mAh 3299
# mix2-2 5.99" 835 6G 128G 3400mAh 3599
# mix2-3 5.99" 835 6G 256G 3400mAh 3999複製代碼
# 默認的模式匹配中就已經包含了 if 的意思了
awk '/2499/' mi_info
# mi6-1 5.15" 835 6G 64G 3350mAh 2499
# minote3-1 5.5" 660 6G 64G 3500mAh 2499
awk '$5=="256G"' mi_info
# mix2-3 5.99" 835 6G 256G 3400mAh 3999
awk '$1 ~ "note"{print}' mi_info
# minote3-1 5.5" 660 6G 64G 3500mAh 2499
# minote3-2 5.5" 660 6G 128G 3500mAh 2899
awk '$0 ~ /note/{print $1, "MEMSIZE->", $3, "Price->", $NF}' mi_info
# minote3-1 MEMSIZE-> 6G Price-> 2499
# minote3-2 MEMSIZE-> 6G Price-> 2899
## -f 示例
# cat awkfile
/note/{
test="price is"
print $1, test, $NF
}
awk -f awkfile mi_info
# minote3-1 price is 2499
# minote3-2 price is 2899
## -v 示例
awk -v test="price is" '/note/{ print $1, test, $NF}' mi_info
# minote3-1 price is 2499
# minote3-2 price is 2899複製代碼
++ -- 增長與減小(前置或後置)
^ ** 指數(右結合性)
! + - 非、一元加號、一元減號
* / % 乘、除、餘數
+ - 加、減
< <= == != > >= 比較
&& 邏輯AND( 簡寫)
|| 邏輯OR( 簡寫)
?: 三元條件式
= += -= *= /= %= ^= **= 賦值( 右結合性)
$ 後面跟數字,表示獲取指定列的域,0 表明取整條記錄
ARGC 命令行參數個數
ARGV 命令行參數構成胡數組
FILENAME 當前輸入文件的文件名
FS 設置輸入域分隔符,等價於命令行 -F選項
OFS 輸出域分隔符
RS 控制記錄分隔符
ORS 輸出記錄分隔符
NF 瀏覽記錄的域的個數
NR 已讀的記錄數
FNR 與NR功用相似,不一樣的是awk每打開一個新文件,FNR便從0從新累計
IGNORECASE 是否忽略大小寫
記錄 默認指的是每行,由於記錄分隔符(RS)是換行符
域 默認指的是每一個單詞,由於域分隔符(FS)是空格
awk 經過內建變量 ARGC ( 參數計數 ) 與 ARGV ( 參數向量,或參數值 ) ,讓命令行參數可用。
cat showargs.awk
BEGIN {
print "ARGC= ", ARGC
for (k=0; k<ARGC; k++) {
print "ARGV["k"]=["ARGV[k]"]"
}
}
awk -f showargs.awk mi_info
# ARGC= 2# ARGV[0]=[awk]
# ARGV[1]=[mi_info]
## 求和
awk '{sum+=$NF} END {print "Sum = ", sum}' mi_info
# Sum = 21693
## 求平均
awk '{sum+=$NF} END {print "Average = ", sum/NR}' mi_info
# Average = 3099
## 求最大值
awk 'BEGIN {max = 0} {if ($NF>max) max=$NF fi} END {print "Max=", max}' mi_info
# Max= 3999
## 求最小值(min的初始值設置一個超大數便可)
awk 'BEGIN {min = 1999999} {if ($NF<min) min=$NF fi} END {print "Min=", min}' mi_info
# Min= 2499
# 求訪問次數的Top 10 Resource,能夠根據此進行優化
cat logs/`date +%u`.log | grep -v '172.16' |grep -v '127.0.0.1' \
| awk '{ if(index($1,"219.141.246")!=0) print $2; else print $1 }' \
| sort | uniq -c | sort -n | tail -n 10
# awk的範圍模式也是封閉範圍。
# 在全部記錄中他們會順序進行屢次匹配,第一次匹配完後還能夠進行下面接下來的第二次、第三次可能的匹配範圍。
# 若是開頭匹配到了,可是沒有結尾的話,會把整個文件記錄的末尾看成是此次匹配的結尾做爲範圍
awk '/mi6/,/note/ {print NR, $1, $NF}' mi_info
# 1 mi6-1 2499
# 2 mi6-2 2899
# 3 minote3-1 2499
# 忽略大小寫
awk '{IGNORECASE=1}; $1~"MI" {print NR,$0}' mi_info
# "全部信息"
# 每 5 行合併爲 1 行
BEGIN {
a = 0;
s = "";
}
{
a += 1;
s = s" | "$0
if (a % 5 == 0) {
print s;
s = "";
}
}
END {
print s
}
awk 'BEGIN{a="100"; b="10test10"; print (a+b+0);}'
# 110
# 只須要將變量經過 "+" 鏈接運算。自動強制將字符串轉爲整型。
# 非數字變成 0,發現第一個非數字字符,後面自動忽略。
awk 'BEGIN{a=100;b=100;c=(a""b);print c}'
# 100100
# 只須要將變量與 "" 符號鏈接起來運算便可。
awk 'BEGIN{a="a";b="b";c=(a+b);print c}'
# 0
# 字符串鏈接操做通"二","+"號操做符。模式強制將左右2邊的值轉爲 數字類型。而後進行操做。複製代碼
## 數組示例
awk 'BEGIN{x=0;i=0;} \ {name[x++]=$1;} \ END { for(;i<NR;i++){ print "name["i"] is",name[i]} }' mi_info
# name[0] is mi6-1
# name[1] is mi6-2
# name[2] is minote3-1
# name[3] is minote3-2
# name[4] is mix2-1
# name[5] is mix2-2
# name[6] is mix2-3複製代碼
^ 行首定位符
$ 行尾定位符
. 匹配除換行以外的單個字符
\* 匹配0個或者多個前導字符(這裏是前導字符0或者多個,任意一個或多個字符,使用 .* )
+ 匹配一個或者多個前導字符
? 匹配0個或者1個前導字符
[] 指定字符中的任意一個字符,好比[Ll] [a-z]
[^] 上面同樣,不匹配的字符
AA|BB 匹配AA或者BB
(AB)+ 匹配一個或者多個AB組合,好比AB,ABAB,ABABAB...
\* 匹配*自己
& 保存查找匹配到的串,能夠用在後面的替換中 s/love/**&**/
sub(/reg/,替換串[,目標串])
gsub(/reg/,替換串[,目標串])
index(str,sub_str) 返回sub_str第一次在str中出現的位置(偏移量從1開始)
length(str) 返回字符串的字符個數
substr(str,start_pos[,length]) 返回子串,若是沒有length,就到串的末尾
match(str,/reg/) 返回正則匹配在字符串中的位置,同時設置RSTART和RLENGTH的值
split(str,arr_name[,split_sig])
atan2(x,y)
cos(x)
exp(x)
log(x)
sin(x)
sqrt(x)
int(x)直接捨去小數,保留整數部分
rand() 產生隨機數(0~1) srand(x) 初始化隨機數種子
默認狀況下每次調用rand(),結果都會產生相同的隨機數,這時候須要調用srand()從新產生一個種子,後面的隨機數纔不一樣
## 函數示例
awk 'BEGIN{ print rand(),rand(),srand(),rand(),rand();}'
# 0.237788 0.291066 1 0.215969 0.629868
awk 'BEGIN{ print rand(),rand(),srand(),rand(),rand();}'
# 0.237788 0.291066 1 0.556348 0.749557
awk 'BEGIN{ print rand(),rand(),srand(),rand(),rand();}'
# 0.237788 0.291066 1 0.0931369 0.835396複製代碼
## 語句和循環示例
(a) if
# 在條件模式中,if是隱含的模式了,而條件語句if也能夠按照須要直接聲明出來的句式相似於
if () {} else if () {} else {}
awk '{ if( $1 ~ /note/) { print "note related."} else if ( $1 ~ /mix/ ) { print "mix related."} else { print "other ..."} }' mi_info
# other ...
# other ...
# note related.
# note related.
# mix related.
# mix related.
# mix related.
(b) while
# 句法
while () {}
awk 'BEGIN { i = 0; count = 0; } { while ( i < NR ) { i ++; if ( $1 ~ /note/ ) { count++; print NR, $1,$4,$5 } } } END { print "Count:",count }' mi_info
# 3 minote3-1 6G 64G
# 4 minote3-2 6G 128G
# Count: 2
(c) for
# 普通for循環,句法
for( ; ; ){}
awk 'BEGIN { i = 0;} { for(;i<NR;i++) { if( $1 ~ /note/ ){ print NR,$1,$4,$5,"~~" } }}' mi_info
# 3 minote3-1 6G 64G ~~
# 4 minote3-2 6G 128G ~~
(d) break continue
# 同C/C++語言同樣,是做用於跳出循環體和跳出本次循環的關鍵字。
### 程序控制語句
(a) next
# 從文件中讀取下一行輸入,而後從awk腳本頂部開始從新執行。同continue效果也有點類似,只不過這裏是做用於awk工具在對每行操做的自動「循環」中的
(b) exit
# 中斷記錄的處理,可是不可以跳過END語句塊。exit能夠帶一個範圍爲0~255的退出參數,約定0表示成功,這個退出參數實際就傳遞給了$?表示執行的結果
awk '{ if ( $1 ~ /note/ ) { print NR,$1,$7,"skip"; next; } if ( $1 ~ /mix/ ) { print NR,$1,$7,"exit will"; exit 3; } print NR,$1,$7,"after if..."; } END { print "Fininal should be called here..." }' mi_info
# 1 mi6-1 2499 after if...
# 2 mi6-2 2899 after if...
# 3 minote3-1 2499 skip
# 4 minote3-2 2899 skip
# 5 mix2-1 3299 exit will
# Fininal should be called here...
echo $?
# 3複製代碼
## (a) 支持 > >> 重定向符號使用的時候做爲文件名參數須要使用 "" 括起來,getline能夠用於輸入重定向來得到輸入信息
awk 'BEGIN { "date" | getline date; print "The date is",date > "date.file"}'
cat date.file
# The date is Wed Dec 28 18:43:39 HKT 2016
## (b) 管道 |
awk '/note/ { print $0 | "grep 2499" }' mi_info
# minote3-1 5.5" 660 6G 64G 3500mAh 2499
## (c) system 函數。能夠進行系統命令調用
awk 'BEGIN { system("whoami") }'
# root
## (d) printf 格式化輸出信息,跟C語言的相似
awk 'BEGIN { printf "Hello, %s, you are %d years old.\n","Nicol TAO","23"}'
# Hello, Nicol TAO, you are 23 years old.複製代碼
最後總結一下awk使用經驗:
若是處理邏輯複雜,就將代碼寫進文件裏,再經過 -f 引用;這樣可防止敲錯代碼。
靈活調用系統命令,內置變量,使代碼更簡潔。
處理大文件時,多審覈優化邏輯代碼,可有效提升執行效率。
本文首發於公衆號「小米運維」,點擊查看原文