歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

Linux之強大的awk

awk簡介

awk是Linux中的一個命令,用來做文本處理與分析,功能簡單強悍,同時它也是一門編程語言。
awk處理文本文件時,以行為單位,可以高效的對日志文件進行處理。

awk的man文檔簡介摘要:

NAME

   gawk - pattern scanning and processing language  //awk其實是gawk,文本匹配查詢和
處理語言,

SYNOPSIS

    gawk [ POSIX or GNU style options ] -f program-file [ -- ] file ...
    gawk [ POSIX or GNU style options ] [ -- ] program-text file ...

    pgawk [ POSIX or GNU style options ] -f program-file [ -- ] file ...
    pgawk [ POSIX or GNU style options ] [ -- ] program-text file ...


    Gawk is the GNU Project?. implementation of the AWK programming language.  It
    conforms to the definition of the lan-
    guage in the POSIX 1003.1 Standard.  This version in turn is based on the 
description in The  AWK  Programming  Lan-
    guage,  by  Aho,  Kernighan, and Weinberger, with the additional features
 found in the System V Release 4 version of
    UNIX awk.  Gawk also provides more recent Bell Laboratories awk extensions, 
and a number of GNU-specific extensions.




【釋】第一段英文大致說了,Gawk是GNU(通用許可開源)項目,源於unix awk,並支持更多的貝爾實驗
室awk的擴展。看起來Linux中的awk(gawk)是很牛叉的。廢話不多說,馬上就來認真學習一下。

使用方式

命令常見使用方式如下

方式一:awk [options] 'script' [var=value] file(s) 
方式二:awk [options] -f scriptfile [var=value] file(s)

【解釋】 其中 options是命令選項。方式一:'script'指awk的處理程序,執行程序。var=value是
變量,file(s) 表示被處理的文本文件,可以一次性處理多個文本文件。方式二中-f scriptfile
表示將處理程序放到一個文本中,當作腳本文件。

選項-F fs

指定分隔符,awk中的默認分隔符是空格 ” “,默認以空格符分隔行文本。

選項在man文檔中的摘要

-F fs
   --field-separator fs //指定分隔符
          Use fs for the input field separator (the value of the FS prede-
          fined variable).  

eg

[案例1] //默認字段分隔符為" "
# echo "hello hh"| awk '{print $1;print $2}'
hello
hh

【解釋】 案例中,並沒有出現-F選項,此時默認分隔符為" " 將hello hh分隔為兩段,再分別打印
字段一:“hello”,再打印字段二:“hh”。$1和$2是位置變量,分別表示第一個字段和第二個字段。

[案例2]
# echo "hello:hh"| awk -F :  '{print $1;print $2}'
hello
hh

【解釋】通過-F指定分隔符為":" 分隔 hello:hh ,然後同 [案例1]

位置變量 0,1,$2..

n是位置參數,表示第n個字段或者說分隔後第n位置上的字符串0表示整行
NF:表示倒數第一個位置上的字符串。NF-1表示倒數第二個位置,以此類推

eg
[案例1]
# cat pass.log //要處理的目標文件 pass.log 筆者截取了一段/etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

# awk -F : '{print ;}' pass.log 
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

【解釋】 "print;" 默認也是打印整行,同$0 


[案例2]
# awk -F : '{print $0;}' pass.log 
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

【解釋】 "print $0;" 打印整行

eg

# awk -F : '{print $1 "   "  $NF;}' pass.log 
root   /bin/bash
bin   /sbin/nologin
daemon   /sbin/nologin

【解釋】打印被 ":" 分隔後第一個位置上的字符串,然後再打印最後一個位置,中間輸入空格方便查看

選項-f program-file

將腳本寫到文本文件中去,通過選項-f將awk命令的處理程序指定到某個腳本文件中,當處理程序復雜的時候通常采用這種方式。

man文檔摘要

-f program-file 
    --file program-file //--file 和-f效果相同
         Read the AWK program source from the file program-file,  instead
         of  from  the  first  command  line  argument.   Multiple -f (or
         --file) options may be used.

eg

[案例一]

# vi sc.awk //編輯腳本
{print $1 "   "  $NF} //將執行文件寫入 sc.awk中
//vi編輯器,保存sc.awk文檔退出。


# awk -F : -f sc.awk  pass.log  //pass.log和前文中pass.log相同。
root   /bin/bash
bin   /sbin/nologin
daemon   /sbin/nologin

【解釋】 將前文中案例 【#awk -F : '{print $1 "   "  $NF;}' pass.log】 中的
'{print $1 "   " $NF;}'寫到了 sc.awk中,此時,處理程序不用再用放在單引號''中了。

【注意】如果對vi不熟悉,可以用echo "{print $1 "   "  $NF}" >sc.awk  來新建程序文件

awk程序結構

awk處理程序由 BEGIN,主體,END 構成
BEGIN語句塊 BEGIN{}

eg

# vi sc.awk //編輯腳本
# awk program's Begin block  //【注意】這句話是腳本裡的注釋,不是命令
BEGIN{print "here is the awk begin!!"} //BEGIN語句快
{print $1 "   "  $NF}    


# awk -F : -f sc.awk  pass.log 
here is the awk begin!! //BEGIN語句塊 的輸出效果 
root   /bin/bash
bin   /sbin/nologin
daemon   /sbin/nologin

【解釋】 上面案例可以看出,BEGIN語句塊中的代碼會在程序最前面執行。且只執行一次。

主體部分,主題部分用 {} 表示,寫在BEGIN之後,END之前。前文中使用的’{print}’都是程序主體。

eg

# vi sc.awk //編輯腳本
# awk program's main block //【注意】這句話是腳本裡的注釋,不是命令
BEGIN{print "here is the awk begin!!"}
{
        print "this is main"
        print $1
}

# awk -F : -f sc.awk  pass.log 
here is the awk begin!!
this is main    // 執行 主體部分中的 print "this is main"
root            // 執行 主體中的print $1
this is main
bin
this is main
daemon


【解釋】 主體中的代碼,沒處理一行就會被執行一次。

END語句塊
和BEGIN語句塊相似 END{} 命令最後會被執行,也僅被執行一次

eg

# vi sc.awk //編輯腳本
# awk program's end block //【注意】這句話是腳本裡的注釋,不是命令
BEGIN{print "here is the awk begin!!"}
{
    print $1
}

END{    
    print "ok,awk operate to the end"   //end模塊打印語句
}

# awk -F : -f sc.awk  pass.log 
here is the awk begin!!
root
bin
daemon
ok,awk operate to the end   //end模塊的執行效果

變量

在awk的處理程序中可以申明變量,和使用變量。
申明方式 varName=varValue

eg | 通過變量,計算文件的行數

# vi sc.awk //編輯腳本
# awk program's var block //【注意】這句話是腳本裡的注釋,不是命令
BEGIN{
        nu=0;           //申明一個變量表示行號
}

{
    nu ++;              //每處理一行數據,行號自動增加1
    print nu " " $0    //輸出行號和 第一個字段
}

END { 
    print "totall lines is " nu;    //最後打印總共的行數,即最後一行行號
}

# awk -F : -f sc.awk  pass.log 
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
totall lines is 3

【解釋】 nu=0為變量申明,使用的時候,直接使用nu即可。不用像bash裡用$打頭

命令變量

我把它叫做命令變量,是因為變量是從命令中傳入的,而並非awk命令中自定義的。
從命令外部引入變量,可以通過命令選項 [var=value] 把shell中的變量引入到awk處理程序中

方式一: awk [options]  -v varname=varvalue 'script' file(s) 
方式二: awk [options] 'script' varname=varvalue  file(s) 

【解釋】方式一是通過-v選項傳入,方式二是 man手冊裡的選項[--]

eg | 方式一

# awk -F : -v  aa="haima"  '{print aa " " $1}' pass.log 
haima root
haima bin
haima daemon

【解釋】 {print aa " " $1} 打印了命令中傳入的變量aa,aa的值是haima。然後再打印 $1

eg | 方式二

# awk -F :  '{print aa " " $1}' aa="haima"  pass.log 
haima root
haima bin
haima daemon

內置變量

內置變量是awk程序中系統自帶的變量,分別都有各自的意義

FS 字段分割符,在程序中【FS=":";】相當於 命令中使用 【-F :】
IGNORECASE 是否忽略大小寫,IGNORECASE=true則匹配時不區分大小寫 
NF 表示字段數,在執行過程中對應於當前的字段數 
NR 表示記錄數,在執行過程中對應於當前的行號
OFS 輸出字段分隔符(默認值是一個空格)
ORS 輸出行記錄分隔符(默認值是一個換行符)

IGNORECASE在筆者系統上沒測試通過 centos6.5

eg | FS

# vi sc.awk 
# awk program's FS  //【注意】這句話是腳本裡的注釋,不是命令
BEGIN{
        FS=":" //用HAIMA 去分割,
}

{
        print $1
}

# awk -f sc.awk  pass2.log 
root
bin
daemon

【解釋】 FS=":" 需要在BEGIN中指定,效果與令行中使用 -F :相同

eg | NF NR

# vi sc.awk 

# awk program's NF NR //【注意】這句話是腳本裡的注釋,不是命令
{
        print  NR   " totall fileds" NF // 打印行號,打印切割後的段
}

END{
        print "line numbers:" NR //END中能打印到行數

 }

# awk -F:  -f sc.awk pass.log 
1 totall files:7
2 totall files:7
3 totall files:7
line numbers:3

【注意】 在END中不能打印到字段數NF

eg|OFS ORS

# awk -F : 'BEGIN{OFS="|";ORS=" LinEnd\n"}{print $1, $2;}'  pass.log 
root|x LinEnd
bin|x LinEnd
daemon|x LinEnd


【解釋】 在BEGIN模塊中申明了OFS="|",輸出字段分隔符是“|” ,而 ORS=" LinEnd\n" 申明了輸出
行分隔符為"linEnd\n"  其中"\n"是 換行符,如果沒有“\n” ,將直接輸出一行

【注意】 “{print $1, $2;}” 時,必須用 "," 連接。如果 {print $1 $2} 字段分隔符將失效。因
為空格在awk程序中可以連接兩個字符串。

awk的工作原理

通過前面的案例,已經大概了解awk的功能。下面再總結一下awk的工作流程。

awk 'BEGIN{ commands } pattern{ commands } END{ commands }'  

第一步:執行BEGIN{ commands }語句塊中的語句;

第二步:從文件或標准輸入(stdin)讀取一行,然後執行pattern{ commands }語句塊,它逐行掃描
文件,從第一行到最後一行重復這個過程,直到文件全部被讀取完畢。

第三步:當讀至輸入流末尾時,執行END{ commands }語句塊。 

BEGIN語句塊在awk開始從輸入流中讀取行之前被執行,這是一個可選的語句塊,比如變量初始化、打
印輸出表格的表頭等語句通常可以寫在BEGIN語句塊中。

END語句塊在awk從輸入流中讀取完所有的行之後即被執行,比如打印所有行的分析結果這類信息匯總
都是在END語句塊中完成,它也是一個可選語句塊。 

pattern語句塊中的通用命令是最重要的部分,它也是可選的。如果沒有提供pattern語句塊,則默認
執行{ print },即打印每一個讀取到的行,awk讀取的每一行都會執行該語句塊。

【注意】awk中的內置變量。不同的內置變量的作用域有所不同。使用的時候,需要注意在awk執行的某
些階段,該內置變量是否可用。

流程控制

if … else if… else
awk可以用類似java中的if語句來做邏輯控制

語法結構如下:

if(表達式)
    {語句1} 
else if(表達式) 
    {語句2}
else
    {語句3} 

eg

# vi nsc.awk  //新建一個awk程序
# test for  "if.. else if.. else " //程序注釋,非指令
{
 if(NR%2==0)
    { print $2 " evenline"}    //偶數行就 輸出第二個字段和 evenline
else if (NR%3==0) 
    {print $3 " thrirdline"} //被3整除的行,就輸出第三個字段 和 thrirdline
else
     {print $NF " othersLine"} //其他就輸出 最後一個字段 和 othersLine

# awk -F :  -f nsc.awk  /etc/passwd 
/bin/bash othersLine
x evenline
2 thrirdline
x evenline
/sbin/nologin othersLine

循環語句

awk也支持for,while,do..while三種循環語句,語法與java中基本相同

for 循環

格式: for(變量;條件;表達式) {語句}

# awk 'BEGIN{ total=0; i=0; do {total+=i;i++;} while(i<=100) print total; }'
5050

while循環

格式:while(表達式) {語句}

# awk 'BEGIN{ test=100; total=0; while(i<=test){ total+=i; i++; } print total; }'
5050

do..while

格式:do {語句} while(條件)

# awk 'BEGIN{ total=0; i=0; do {total+=i;i++;} while(i<=100) print total; }'
5050

# awk 'BEGIN{ total=0; i=1000; do {total+=i;i++;} while(i<=100) print total; }'
1000    //雖然i一開始就滿足i<=100  但do中還是執行了一次,所以打印total為 1000

【注意】do.. while 循環語句至少一次循環,先執行do裡面的內容,再判斷,所以這樣。

break,continue

awk同樣也支持break和continue控制。break和continue必須在循環體中使用。
break是直接跳出循環,不再執行循環
continue是跳到下次循環

next控制

next 從單詞的意思看起來和我continue相似。確實如此,next具有continue很相似的功能,只是next是跳出本行數據處理,處理下一行數據,而continue是跳出本次循環,執行下次循環。

eg | 跳過 行號為6-9行的數據

# awk -F :  '{if(NR>5&&NR<10)next; print NR, $1}' /etc/passwd
1 root
2 bin
3 daemon
4 adm
5 lp
10 uucp
11 operator
12 games
13 gopher
14 ftp
15 nobody

【解釋】 if(NR>5&&NR<10)next; 當行號在 6-10,直接跳過本行數據的處理。
【注意】 因為next是跳過某個記錄,或者說某行數據的處理,所以next只能出現在awk的主體中,不能
出現在BEGIN 塊,也不能出現在 END模塊中,會報錯。

exit 控制

退出當前還沒有執行的主體內容,如果END塊中有內容,則執行END塊中的內容。此時就會出現三種情況。

情況一:exit在BEGIN塊中使用,從exit當前位置退出,直接執行END塊
情況二:exit在主體中使用,從exit當前位置退出,直接執行END塊
情況二:exit在END塊中使用,從exit當前位置退出,已經退出END快,所以不再執行了。

eg | exit在主體中使用。

# awk -F :  '{if(NR>5){exit};print NR, $1}END{print "end"}' /etc/passwd
1 root
2 bin
3 daemon
4 adm
5 lp
end

【解釋】當行號NR>5時,退出主體,再執行print "end"

eg | exit在BEGIN或END中使用

# awk -F : '{print"begin";exit;print"hh"}{print NR, $1}END{print "end"}' /etc/passwd
begin
end

【解釋】print "begin" 執行完後,直接跳到了END模塊執行 print "end"

awk中的正則表達式

awk可以利用正則表達式來篩選數據,以達到更強大的處理能力

用於記錄篩選

記錄篩選,以一條記錄作為篩選的單位

awk [options] ‘script’ [var=value] file(s) 在此主體格式不變的情況下,在script前面加入篩
選,此時,’script’ 程序形式變成:’(/express/){awk處理程序}’,其中/express/是正則表達式

手動創建一個文件target.log 並寫好內容如下

# cat target.log //查看target.log
27.19.74.143 - - GET /static/image/common/faq.gif HTTP/1.1" 200 1127
110.52.250.126 -  GET /data/cache/style_1_widthauto.css?y7a HTTP/1.1" 200 1292
27.19.74.143 - - GET /static/image/common/hot_1.gif HTTP/1.1" 200 680
27.19.74.143 - - GET /static/image/common/hot_2.gif HTTP/1.1" 200 682
27.19.74.143 - - GET /static/image/filetype/common.gif HTTP/1.1" 200 90
110.52.250.126 -  GET /source/plugin/wsh_wx/img/wsh_zk.css HTTP/1.1" 200 1482
110.52.250.126 -  GET /data/cache/style_1_forum_index.css?y7a HTTP/1.1" 200 2331
110.52.250.126 -  GET /source/plugin/wsh_wx/img/wx_jqr.gif HTTP/1.1" 200 1770
27.19.74.143 - - GET /static/image/common/recommend_1.gif HTTP/1.1" 200 1030
110.52.250.126 -  GET /static/image/common/logo.png HTTP/1.1" 200 4542
110.44 -  GET /static/image/common/logo.png HTTP/1.1" 200 4542

【注意】 最後一行數據是  不正常的數據。ip 的格式不對

eg | 篩選出包含.gif的行

# awk '(/gif/){print $0}' target.log 
27.19.74.143 - - GET /static/image/common/faq.gif HTTP/1.1" 200 1127
27.19.74.143 - - GET /static/image/common/hot_1.gif HTTP/1.1" 200 680
27.19.74.143 - - GET /static/image/common/hot_2.gif HTTP/1.1" 200 682
27.19.74.143 - - GET /static/image/filetype/common.gif HTTP/1.1" 200 90
110.52.250.126 -  GET /source/plugin/wsh_wx/img/wx_jqr.gif HTTP/1.1" 200 1770
27.19.74.143 - - GET /static/image/common/recommend_1.gif HTTP/1.1" 200 1030

eg | 篩選出數據不正常的行(target.log中最後一行數據為錯誤數據)

#awk --posix '!/([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}/{print $0}' target.log
110.44 -  GET /static/image/common/logo.png HTTP/1.1" 200 4542  

【解釋】 篩選行,首先選出行中含有IP格式,ip格式用/([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}/
匹配,前面加 ! 表示取非,也就是不含ip格式的行,被打印出來

【注意】筆者查詢了一下, 在gawk 4.0以下版本中使用{n,m}這樣的正則,需要在在前面加上
--re-interval或--posix選項。

用於字段篩選

awk也可通過篩選某個字段,來篩選某一行
在正則表達式前面 n表示n字段滿足正則篩選 n表示第n個字段在正則表達式前面n !~ 表示 n字段不滿足正則篩選n表示第n個字段
使用0也可以對行進行篩選,0表示整行的意思

eg| 用 “:” 切割/etc/passwd,篩選出用戶名長度為4的用戶

# awk --posix -F :  '$1~/^.{4}$/{print $1}' /etc/passwd
root
sync
halt
mail
uucp
dbus
vcsa
abrt
sshd

【注意】筆者查詢了一下, 在gawk 4.0以下版本中使用^$這樣的正則,需要在在前面加上
--re-interval或--posix選項。

eg| 用 “:” 切割/etc/passwd,篩選出用戶名長度不為4的用戶

# awk --posix -F :  '$1!~/^.{4}$/{print $1}' /etc/passwd
bin
daemon
adm
lp
shutdown
operator
games
gopher
ftp
nobody
usbmuxd
rtkit
//此處省略部分結果行

【注意】筆者查詢了一下, 在gawk 4.0以下版本中使用^$這樣的正則,需要在在前面加上
--re-interval或--posix選項。

用在表達式中

正則表達式用在返回值為true/false的表達式中,同樣用~,!~匹配,匹配成功則返回true

eg | 處理/etc/passwd 匹配行數為二十多的

# awk --posix -F :  '{if(NR ~ /^[2][0-9]$/)print NR,$1}' /etc/passwd
20 avahi-autoipd
21 abrt
22 haldaemon
23 gdm
24 saslauth
25 postfix
26 ntp
27 apache
28 pulse
29 sshd

【解釋】 NR為內置變量行號,行號匹配正則表達式為/^[2][0-9]$/,則打印行號NR,第一個字段
Copyright © Linux教程網 All Rights Reserved