1. 程式人生 > 實用技巧 >shell知識點(8)AWK命令詳解

shell知識點(8)AWK命令詳解

AWK介紹

awk(名字來源於三個創始人姓氏首字母)是linux系統下文字編輯工具,是一門程式語言,有自己的基本語法和流程控制、函式。awk簡單高效。

呼叫AWK

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

2.shell指令碼方式
將所有的awk命令插入一個檔案,並使awk程式可執行,然後awk命令直譯器作為指令碼的首行,一遍通過鍵入指令碼名稱來呼叫。
相當於shell指令碼首行的:
#!/bin/sh 可以換成:#!/bin/awk 3.將所有的awk命令插入一個單獨檔案,然後呼叫: awk -f awk-script-file input-file(s) 其中,-f選項載入awk-script-file中的awk指令碼,input-file(s)跟上面的是一樣的。

=========================================================================================================================

一、AWK基本語法

awk通過FS作為每一段文字的分割符(預設空格),在命令列上可以用-F引數指定分隔符; 通過RS
引數指定文字換行符(預設回車,所以是一行行取資料的),通過換行符作為分割來讀取檔案。這裡區別於sed,sed是一行一行讀取檔案的 基本流程 BEGIN{在讀取檔案之前做的操作} {讀取檔案時做的操作} END{全部檔案讀取之後才進行的操作} 常用內建變數
     $0  當前所有欄位
     $1--$n 按照分隔符分割取到的第n列內容
     FS  分隔符(預設空格) awk 'BEGIN{FS=":"}{print $1}' /etc/passwd ;等價於awk -F: '{print $1}' /etc/passwd
     RS  換行符(預設回車) awk 'BEGIN{RS
=":"}{print $1}' /etc/passwd NF 字元列數,當前處理行的分割後的列數 awk -F: '{print NF}' /etc/passwd NR 行號 awk -F: '{print NR ":" $1}' /etc/passwd OFS (預設空格)輸出欄位分隔符 ORS (預設回車)輸出記錄分隔符
   ARGC               命令列引數個數
   ARGV               命令列引數排列
   ENVIRON            支援佇列中系統環境變數的使用
   FILENAME           awk瀏覽的檔名
   FNR                瀏覽檔案的記錄數
   FS                 設定輸入域分隔符,等價於命令列 -F選項

自定義外部變數

     -v:自定義變數
     awk -v host=$HOSTNAME "BEGIN{print host}"

關係操作符

<、>、<=、>=、==、!=、~、!~
比較符<等與其他的語言類似,重點說一下不一樣的
 ~:用來判斷前面的列是否匹配後面的內容。例如awk -F: '$7 ~ /^\/bin/{print $0}' /etc/passwd(判斷第7列是否以/bin開頭,如果是列印該列)
!~:不匹配

輸出print和printf

     print:直接輸出 awk -F: '{print $1 ":" $2}' /etc/passwd
     printf:格式化輸出(printf是一個函式,需要用到())
     awk -F: '{printf(hello %s:%s\n),$1,$2}'
     注意:printf需要手動增加\n來換行。使用%s來格式化,printf()外加入要替換的變數

二、AWK的流程控制

條件:

if語句 if(expression){action1}else{action2}
     例如:產生10個數seq 10,通過if語句判斷是單數還是雙數
     seq 10 |awk '{if($0%2==0){print $0"是雙數"}else{print $0"是單數"}}'
 
如果只需要一個if分支,可以省略前面的if,比如awk -F: '$3<-10 {print $1}' /etc/passwd
迴圈:
while語句:while(expression){action}
     例子:使用:分割/etc/passwd,並將每一列前加上列號
     awk -F: '{i=1;while(i<=NF){print i":"$i;i++ }}' /etc/passwd
for語句:
第一種方法:for(i=0;i<=10;i++){action}
     例子:使用:分割/etc/passwd,並將每一列前加上列號
     awk -F: '{for(i=1;i<=NF;i++){print i":"$i}}' /etc/passwd
 
第二種方法:for(value in array){action}
當value在array的key中,進行下面的操作。awk的陣列類似python中的字典。
     例子:統計/etc/passwd第7列的值及對應的個數
     awk -F: '{a[$7]++}END{for(i in a)if(i!=""){print i":"a[i]}}' /etc/passwd
     說明:a[$7]:將$7作為陣列a的key,然後統計對應的個數;然後遍歷for(i in a),判斷i是否在陣列a的key中;如果存在則列印a[i],a[i]為對應key的值,這裡指個數。
陣列:
array[1]="hello"
array["name"]="Jack"
陣列類似python的字典,array[key值]="value值";key為索引,可以是數字也可以是字串。
陣列元素的刪除:delete array["key"]
 
     例子:定義了陣列a的三個值,並列印結果檢視
     awk 'BEGIN{a[1]="hello";a[2]="word";a["name"]="meitian";for(i in a){print "key為"i":value為"a[i]}}'

三、awk函式

內建函式 1.算術函式:
int(x) 返回x的整數部分的值,值不會四捨五入,只是取整
sqrt(x) 返回x的平方根
rand() 返回偽隨機數r,其中0<=r<1,(偽隨機數指返回的值都是上一次返回的同一個隨機數)
srand(x) 建立rand()新的種子數,如果沒有指定就用當天的時間(使用srand()可以使得rand()返回不同的隨機數)
     例子:rand()產生一個隨機數,通過srand()產生新的種子數,然後再差生一個隨機數
     awk 'BEGIN{print rand();srand();print rand()}'
2.字串函式:
sub("要替換的字串","替換後的字串值"):替換匹配到的第一個文字
     echo "hello world world" | awk '{sub("world","meitian");print $0}'
gsub("要替換的字串","替換後的字串值" ):開啟全域性替換,替換文字中所有匹配到的字串
     echo "hello world world" | awk '{gsub("world","meitian");print $0}'

示例:

[root@t-enter ~]# echo "hello world world" | awk '{gsub("world","meitian");print $0}'
hello meitian meitian
[root@t-enter ~]# echo "hello world world" | awk '{sub("world","meitian");print $0}'
hello meitian world
index("a","b"):返回字串b在字串a中開始的位置
     awk 'BEGIN{print index("hello world","world")}'
length("s"):返回字串s的長度,當沒有指定s時,返回$0的長度
      awk -F 'BEGIN{print length("hello world")}{print lenght()}' /etc/passwd
match("s","r"):如果正則表示式r在s中匹配到,則返回出現的起始位置,否則返回0
     awk 'BEGIN{print match("hello world","[wo]")}'
split(s,a,sep) 使用sep將字串s分解到陣列a中,預設sep為FS。
例子:使用o做為分隔符,將"hello world"進行分割儲存到陣列a中
awk 'BEGIN{print split("hello world",a,"o");for(i in a){print a[i]}}'
[root@t-enter ~]#  awk 'BEGIN{print split("hello world",a,"o");for(i in a){print a[i]}}'
3
hell
 w
rld
toupper(s):將所有小寫字母轉換成大寫字母
     echo "hello world" |awk '{print toupper($0)}'
tolower(s):將所有大寫字母轉換成小寫字母
     echo "HELLO WORLD" |awk '{print tolower($0)}'
自定義函式:
function 函式名(引數1,引數2,...){語句;return 表示式}
     例子:求和
     awk 'function sum(a,b){total=a+b;return total}BEGIN{print sum(2,3)}'
注意:函式必須寫在BEGIN{}{}END{}的花括號之外的地方,不能放在任何{}內,否則會報錯`return' used outside function context

四、實戰

1.獲得eth0的IP地址
ifconfig eth0 | awk -F":| +" '/inet addr:/{print $4}'
ifconfig eth0的結果:
[root@t-enter ~]# ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:0C:29:F3:13:BB  
          inet addr:192.168.174.5  Bcast:192.168.174.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fef3:13bb/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1531377 errors:0 dropped:0 overruns:0 frame:0
          TX packets:288458 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:1593454324 (1.4 GiB)  TX bytes:24880369 (23.7 MiB)
          Interrupt:19 Base address:0x2000 

說明:

1.多個字元作為分隔符(比如例子中的冒號和空格),可以使用|來區分;或者直接使用正則來作區分。比如例子中的-F":| +"可以寫成-F"[ :]+" 2.如果要過濾多個相同分隔符,可以使用正則的+。表示1個或多個 3.awk中可以使用'/操作匹配到該內容的行/{匹配到前面的行後進行的操作}'來選擇某些想要的行。比如例子中需要取匹配到“inet addr:”的行,列印第4列,//中的為正則表示式,如果有/等需要使用\進行轉義 2.統計tcp網路連線數
netstat -an |awk  '/^tcp/{a[$NF]++}END{for(i in a){printf("%s:%d\n",i,a[i])}}'
說明:與上面的例子大同小異,/^tcp/表示只處理以tcp開頭的行。$NF表示最後一列

五、在實戰中可能用到的注意點

1.awk需要對檔案進行處理。不需要處理檔案的可以把列印命令寫在BEGIN裡(例如:awk -v name=meitian 'BEGIN{print name}') 2.可以對ls等命令結果進行處理 例子:對包含conf的檔案按照.進行分割,並將分割結果用冒號進行連線 ls |grep conf |awk -F. '{print $1 ":" $2}' 3.awk使用-v定義變數,但是awk中引用變數時直接使用變數名,不需要在變數前加$ 4.將awk中的結果賦值給變數傳遞給shell中用 方法一:可以使用eval()函式來將列印的結果轉換成變數。eval會將列印的值當做命令來進行處理,shell中定義變數的格式:變數名=變數值 例子:有個檔名為search.contract-0.0.5-SNAPSHOT.jar,按照-分割。將-前面的存為name變數,中間的版本號存為version變數。 eval $(ls |grep search.contract-0.0.5-SNAPSHOT.jar |awk -F"-" '{printf("name=%s;version=%s\n",$1,$2)}')
[root@t-enter tmp]# eval $(ls |grep search.contract-0.0.5-SNAPSHOT.jar |awk -F"-" '{printf("name=%s;version=%s\n",$1,$2)}')
[root@t-enter tmp]# echo $name
search.contract
[root@t-enter tmp]# echo $version
0.0.5
方法二:如果只想儲存一個變數,可以通過變數名=$(操作print變數值)的方法來儲存 例如提取ifconfig eth0本機中的IP地址儲存到變數a中 a=$(ifconfig eth0 |awk -F":| *" '/inet addr/{print $4}')
[root@t-enter tmp]# a=$(ifconfig eth0 |awk -F":| *" '/inet addr/{print $4}')
[root@t-enter tmp]# echo $a
192.168.174.5
5.只對包含某些內容的行進行操作 awk '/要匹配的內容/{進行的操作}' file,//內可以放入正則表示式,注意對一些特殊字元進行轉義,比如“[]\“ 6.awk不能直接修改原始檔,可以通過重導向輸出結果來修改原檔案。>(全部覆蓋檔案內容)或>>(追加到檔案) awk '/^root/{print $0 >"passwd"}' passwd 注意:重導向輸出的檔名要用雙引號括起來,否則會報錯 7.awk使用雙引號作為分隔符,可以使用單引號括起來,'"'