shell--細說awk

1 awk 簡述

awk 是 Linux/UNIX 下的用來操縱數據和產生報告的程序語言。 Nawk 是新的版本, gawk
是 Gnu 版本。awk 可用於命令行的簡單操做,也能夠寫入大的應用程序。由於 awk 能夠操縱數據,因此它是 Shell腳本和管理小型數據庫中必需的工具。linux

1.1 編程模型

    awk 執行過程分爲三個階段:讀輸入文件以前執行的代碼段(由BEGIN關鍵字標識);主循環執行輸入文件的代碼段;讀輸入文件以後的代碼段(由END關鍵字標識)。可參考下圖c++

awk 編程模型

1.2 基本概念

    awk 結構由模版和動做組成。模版用於篩選數據行,一般由正則或條件表達式構成;動做是由一對大括號包裹的代碼組成。他們都是可選的。正則表達式

#模式能夠是正則、條件表達式或兩者的結合。模式不須要寫在大括號內
#模式規定了觸發大括號內的動做的條件

awk '/root/' txt.log #只有模版,輸出含有root的行
awk -F : '$3 == $4' txt.log #只有模版,輸出uid=gid的行
awk -F : '{if($3 == $4) echo print $0}' txt.log #區別於模式,動做要寫在括號內
awk '/root/{print $0}' txt.log #{}中是動做
awk '{print $0}' txt.log #只有動做,表示處理全部輸入行

#範圍匹配 '/root/, /mail/' 等同於sed
awk -F: '/root/, /daemon/' txt.log

#多個語句
awk -F: '/root/{print $0}{print $1}' txt.log #一個模版只能結合一個動做大括號,後一個是新的開始

兩種調用模式:awk -F 域分隔符  'awk程序段'  輸入文件   或   awk   -f xx.awk   輸入文件shell

記錄和域:awk 將文件行定義爲記錄,行中的字符串定義爲域。域之間用空格、Tab鍵或其它字符分割。$爲域操做符,$0表示整行記錄,$1...$n 表示第幾個域,也支持表達式 $(a+b) a,b 爲整數。-F指定域分隔符,能夠是單個字符或正則表達式,默認是空格,Tab被認爲是多個空格。字符串跟變量用雙引號拼接。數據庫

awk -F ':' '{print NF}' txt.log # 一個冒號
awk -F ':+' '{print NF}' txt.log # 爲正則時以貪婪匹配爲準且第一個字符不會被解析爲正則符號

2 變量

    不須要聲明能夠直接使用,類型是由其內容決定的,變量名區分大小寫。編程

     數據類型:數字(整型和浮點型 )、字符串、null、數組數組

    賦值語句跟C語言相同,再也不贅述,默認初始化爲0或空字符。bash

#變量的使用
awk -F ':' 'BEGIN{one=1;two=2;print res} {res+=$(one+two);print res' txt.log

2.1 內置變量

awk 內置了許多有用的變量,可使用man命令細查,下邊列出經常使用的函數

變量名 釋義
ARGC 命令行中參數的個數
AGRIND 命令行中當前文件的位置,下標從0開始
ARGV 命令行參數的數組
ENVIRON 環境變量管理數組
FILENAME 當前文件名
FNR 瀏覽每一個文件的記錄數
FS/RS 域分隔符,默認空格 | 記錄分隔符,默認空格
IGNORECASE 布爾值,爲真則忽略大小寫匹配
NF/NR 當前記錄中域數量,每行有可能不一樣 | 當前記錄數,不一樣於FNR,它記錄的是總行數
OFMT/OFS/ORS 數字輸出格式 | 輸出域分隔符、默認空格 | 輸出記錄分隔符,默認換行符
SUBSEP 數組下標分隔符,默認\034,能夠用來模擬實現二維數組

2.2 數組使用

awk 支持關聯數組,下標也能夠是變量。使用for (key in array) array[key] 來遍歷工具

#數組賦值、刪除【delete】、遍歷【for in】、長度【length()】
awk -F: '{name[NR]=$1;} END{print length(name);delete name[1];for(n in name) print n, name[n]}' txt.log
#注意:name["a"]=3  if("a" in name) print "exist"   in 能夠斷定鍵是否存在

2.3 參數變量

    能夠經過命令行參數將變量傳遞到awk 的BEGIN 塊裏,有兩種方式

#-v 參數傳遞變量
awk -v a=1 -v b="hi dude" 'BEGIN{print a, b}'

#awk 腳本
##======test.awk========
 # {print a, b}
##======================
awk -f test.awk a=1 b="hi dude" txt.log #在begin模塊訪問不了參數變量

3 運算符

    包括整數運算,比較運算符、邏輯運算符 跟C語言相同。只是多了 x~/y/  x!~ /y/ 兩個正則匹配符

#關係運算符 > >= < <= == !=   ~  !~
awk -F: '$0 ~ /root/' txt.log # 正則匹配符
awk -F: '$0 !~ /root/' txt.log # !表示非
awk -F: '$3>=20 {print $1}' txt.log # == !=  

#條件表達式
awk -F: '{max=$4>$3 ? $4 : $3;print $0,max}' txt.log 

#布爾運算符 &&  ||  !
awk -F":+" 'NF == 6 || $1 ~ /root/ {print $0 " show"}' txt.log

#算術運算  + - * /(非整除) %  ^或** ++ --
awk -F: '{print $3 "+10=" $3+10, x+=1}' txt.log 
awk -F: '{print $3 "/2=" $3/3}' txt.log # 保留6位有效小數
awk -F: '/^$/ {++x} END{print "有"x"個空行"}' txt.log # 未初始化的整型默認爲0

4 流程控制

    if 語句同C語言一致,屬於動做語句。

#if 用法示例,多個語句須要大括號,之間分號間隔
awk -F: '{
	if($1 ~/root/) msg="root";\
	else if($1 ~/mail/) msg="mail";\
	else msg="unkonwn";
}END{print msg}' txt.log

循環示例

#while 相似還有do while
awk -F: '{i=1;while($i<=NF){print NF, $i, $i++;}}' txt.log

#for 有兩種模式
for (n in array) 專用於數組
awk -F: '{for(i=1;i<=NF;i++)print NF,$i;}' txt.log

#循環控制
continue break 與C語言同
next 會阻止其在同一個命令塊中後邊的命令,而後從頭執行
exit 會退出整個awk 命令塊,直接跳轉到END模塊(若是有)

5 函數

也能夠自定義函數,使用較少,主要介紹系統函數。

#字符串函數示例

#sub(/正則/,"替換字符","目標字符串,可省略默認是$0"),使用的是貪婪匹配,且只替換第一次匹配
#gsub 與sub 語法格式相同,區別在因而所有替換
awk -F: '{sub(/r.+t/, "===");print}' txt.log #是貪婪匹配

#index(string, substring) 返回子串第一次出現的位置,下標從1開始
awk -F: 'BEGIN {print index("hello", "l")}'

#length(string) 返回字符數,省略參數表示則默認爲$0
#substr(string,start=1,length),省略或超出總長都默認到末尾,不能爲負數
#match(string, /pattern/)返回第一次匹配的字串位置並保存在RSTART中,字串長度保存在RLENGTH,不匹配返回0
awk -F: 'BEGIN {start= match("Good old USA", /[A-Z]+$/);print RSTART,RLENGTH,start}'

#toupper(str), tolower(str) 大小寫轉換,僅gawk支持;sprintf(str) 格式化輸出
#split(string,array,sep=FS),將返回的數組放到array中,默認分隔符爲FS,無匹配則返回整個字符到第一個元素中
awk -F: 'BEGIN{split("2017-11-29",date);print date[1];}'

#時間函數 systime  strftime

#systime() 返回unix時間戳(秒) awk 'BEGIN{print systime()}'
#strftime("格式符" [,時間戳=當前]) 格式符與C庫strftime 一致,可 man strftime 查看
awk 'BEGIN{print strftime("%D")}'

#數學函數 int  rand  srand

#int() 去掉小數部分,沒有舍入
#rand() 生成0-1的隨機小數,每次執行產生的值都同樣
awk '/root/{print rand()}' txt.log #執行兩遍,輸出的結果同樣
#srand(x) 生成rand的種子,x默認是時間戳
awk 'BEGIN{srand()} /root/{print 1 + int(25*rand())}' txt.log

6 重定向和管道

#輸出重定向(>和>>), 文件路徑需要引號包裹,文件一旦被打開,必須明確關閉或等待awk程序結束
awk -F: '$3>20 {print $0>"/tmp/test.log"}' txt.log

#輸入從新定向 getline 函數。它能夠從標準輸入、管道,外部文件獲取下一行輸入。
#會修改NF,NR,FNR,若是獲得一個記錄返回1,達到文件末尾返回0,出現錯誤返回-1
#從管道獲取
awk 'BEGIN{"date"|getline now;print now}' 
awk 'BEGIN{while("ls"|getline)print}'

#從命令行獲取
awk 'BEGIN{printf "What is your name?";\
getline name <"/dev/tty"}\
$1~name{print "found"name"on line",NR"."}\
END{print "See ya,"name"."}' txt.log

#從外部文件獲取
awk 'BEGIN{while(getline < "/etc/passwd">0)lc++;print lc}'

管道:若是在awk中已打開一個管道,那麼在打開下一個管道前必須關閉它,管道符號右邊能夠經過雙引號關閉管道。

system("linux shell")  能夠執行系統命令,正常的執行系統命令須要用雙引號包裹

相關文章
相關標籤/搜索