文本三劍客之awk

文本三劍客之awk

簡介

     awk是一種編程語言,用于在linux/unix下對文本和數據進行處理。數據可以來自標準輸入、一個或多個文件,或其它命令的輸出。它支持用戶自定義函數和動態正則表達式等先進功能,是linux/unix下的一個強大編程工具。它在命令行中使用,但更多是作為腳本來使用。awk的處理文本和數據的方式是這樣的,它逐行掃描文件,從第一行到最后一行,尋找匹配的特定模式的行,并在這些行上進行你想要的操作。如果沒有指定處理動作,則把匹配的行顯示到標準輸出(屏幕),如果沒有指定模式,則所有被操作所指定的行都被處理。awk分別代表其作者姓氏的第一個字母。因為它的作者是三個人,分別是Alfred Aho、Brian Kernighan、Peter Weinberger。gawk是awk的GNU版本,它提供了Bell實驗室和GNU的一些擴展。下面介紹的awk是以GUN的gawk為例的,在linux系統中已把awk鏈接到gawk,所以下面全部以awk進行介紹。

本文將從以下幾個方面來介紹

1、awk的命令格式及選項


1.1 awk的使用格式

(1) 命令行格式        
awk [options] var=value ‘[ BEGIN{ action;… } ] pattern{ action;… } [ END{action;… } ]' file …         
(2) 調用自定義腳本    
awk [options] var=value -f programfile file…

說明了awk命令的格式,先看下面的awk命令

awk -F: -v var=value 'BEGIN{print hello } /root/{printf "%-15s %i\n",$1,$3} END{print byebye}' /etc/passwd

hello                  # 打印BEGIN中的內容
root            0               # 通過/root/匹配查找到的包含root的用戶
operator        11              # 通過/root/匹配查找到的包含root的用戶
byebye                  # 打印END中的內容

上面awk說明:

  • -F: :指定文件作用行的分隔符
  • -v var=value :用戶自己在awk中自己指定的變量
  • BEGIN{ } :可選的語句塊,awk開始從輸入流中讀取行之前被執行,比如變量初始化、打印輸出表格的表頭等語句通??梢詫懺贐EGIN語句塊中
  • END{ } :可選的語句塊,在awk從輸入流中讀取完所有的行之后即被執行,比如打印所有行的分析結果這類信息匯總都是在END語句塊中完成
  • pattern{ action;… } : pattern語句塊中的通用命令是最重要的部分,也是可選的。如果沒有提供pattern語句塊,則默認執行{ print },即打印每一個讀取到的行,awk讀取的每一行都會執行該語句塊
  • printf :awk的輸出格式定義,這里的printf與C語言中的用法基本相同,通過選項指定以特定的格式輸出,接下來會詳細說明
  • $# ,由分隔符分隔的字段(域)標記$1,$2..$n稱為域標識。以下會進行說明,這里先了解一下就好

awk把每一個以換行符結束的行稱為一個記錄 記錄分隔符:默認的輸入和輸出的分隔符都是回車,保存在內建變量ORS和RS中

相信通過上面的awk命令,已經能夠大致了解awk的用法及格式,下面我們就來詳細的介紹一下

1.2 awk的選項

awk的選項[options]比較簡單:

-F fs : 指定輸入文件折分隔符,fs(field separator)是一個字符串或者是一個正則表達式,如-F:。    

-v var=value : 用戶定義一個變量并賦值,在awk當中可用    

-f programfile : 從腳本文件中讀取awk命令

以上是awk較為常用的命令選項,還有更多選項這里不再過多介紹

2、awk的模式


awk的模式匹配

awk中的模式與sed差不多,都是需要通過設定的模式來定位到某個位置,或則是匹配到某個參數。

下面介紹一下awk數據處理的模式有哪些

  • 正則表達式,格式為/regular expression/
  • expresssion: 表達式,其值非0或為非空字符時滿足條件
  • 指定的匹配范圍,格式為pat1,pat2
  • BEGIN/END:特殊模式,僅在awk命令執行前運行一次或結束前運行一次
  • 空模式: 匹配任意輸入行
  • (1)/regular expression/:僅處理能夠模式匹配到的行,需要用/ /括起來
  •  awk '/^UUID/{print $1}' /etc/fstab
  • awk '!/^UUID/{print $1}' /etc/fstab
[root@Centos6 ~]#awk '/^UUID/{print $1}' /etc/fstab

UUID=ef20e35d-b3b0-4bb0-a7f0-b6da5f9478ea
UUID=bad2ae77-157a-4e40-a1c0-8d67af3cc105
UUID=90739410-fd0f-4419-900a-2b981300f2d0
UUID=97172ba1-b115-4e5a-b739-8f2b2b309115

命令將包含UUID的行找到并顯示出來

  • (2)expression: 關系表達式,結果為“真”才會被處理    
  • 真:結果為非0值,非空字符串    
  • 假:結果為空字符串或0值 如:$1 ~ /root/ 或 $1 == "wang",用運算符(匹配)和!(不匹配)。 
  • awk '!0{print $1}' /etc/issue
[root@Centos6 ~]#awk '!0{print $1}' /etc/issue 
CentOS
Kernel

上面命令!0條件為真,才會執行{print $1},反之同樣道理0或條件為假則不會執行{action}

  • (3)/pat1/,/pat2/ 不支持直接給出數字
  • awk -F: '/root>/,/nobody>/{print $1}'/etc/passwd
  • awk -F: '(NR>=10&&NR<=20){print NR,$1}'/etc/passwd
[root@Centos6 ~]#awk -F: '/^root\>/,/^halt\>/{printf "%-10s %d\n", $1,$3}' /etc/passwd

root       0
bin        1
daemon     2
adm        3
lp         4
sync       5
shutdown   6
halt       7

我們看到awk配合printf將用戶名稱從root到halt的行及對應的ID號打印出來,并且在輸出時比較方便,美觀

  • (4)BEGIN/END:執行前運行一次或結束前運行一次
  • BEGIN:讓用戶指定在第一條輸入記錄被處理之前所發生的動作,通??稍谶@里設置全局變量。
  • END:讓用戶在最后一條輸入記錄被讀取之后發生的動作。 awk -F : 'BEGIN {print "user id"}{printf "%-10s %d\n", $1,$3}END{print "end"}' /etc/passwd
[root@Centos6 ~]#awk -F : 'BEGIN {print "user       id"}{printf "%-10s %d\n", $1,$3}END{print "end"}' /etc/passwd

user       id
root       0
bin        1
daemon     2
adm        3
...
部分省略
...
tcpdump    72
ymd        500
end

這里就不在過多說明

  • (5)空模式: 匹配每一行

3 awk中的變量

awk也是一門語言,用戶不僅可以通過選項在awk中自定義變量,其自身包含了許多內置有特定含義的變量,用在awk語句中可以方便我們進行使用 下面對其內置變量進行列舉,并對其進行解釋

變量如下:

3.1 awk內置變量

$# 當前記錄的第#個字段,比如#為1表示第一個字段,#為2表示第二個字段 $0 表示變量當前行的所有文本內容。

  • FS :輸入字段分隔符,默認為空白字符
[root@Centos6 ~]#awk -v FS=':' '{print  $1,FS,$3}' /etc/passwd
root : 0
bin : 1
...
部分省略
...
nobody : 99
dbus : 81

在定義輸入字符段時,-v FS=':' 等價于 -F:

  • OFS:輸出字段分隔符,默認為空白字符
[root@Centos6 ~]#awk -F:  -v OFS=':' '{print $1,$3}' /etc/passwd

root:0
bin:1
daemon:2
...
部分省略
...
rpc:32
rtkit:499
  • RS:輸入文本信息換行符,原換行符仍有效
# 對輸入的文本以空格作為一個行,進行打印 
[root@Centos6 ~]#awk -v RS=' ' '{print }' /etc/issue

CentOS
release
6.9
(Final)
Kernel
\r
on
an
\m
  • ORS:輸出文本信息換行符,用指定符號代替換行符
對輸入的文本以空格作為一個行,輸出文本的換行符用#號代替 
[root@Centos6 ~]#awk -v RS=' ' -v ORS='###' '{print }' /etc/issue  
CentOS###release###6.9###(Final) 
Kernel###\r###on###an###\m
  • NF:字段數量
#每個操作行中的字段數量 
[root@Centos6 ~]#awk '{print NF}' /etc/issue

4
5
  • NR:行號
# 顯示出每個操作行所對應的行號數 
[root@Centos6 ~]#awk '{print NR}' /etc/passwd

1
2
3
4
5
6
7
以下省略
...
  • FNR:對輸入的多個文件分別計行號數
# 對輸入的/etc/issue  /etc/passwd文件分別計行號數 
[root@Centos6 ~]#awk '{print FNR}' /etc/issue  /etc/passwd

1
2
1
2
3
4
5
以下省略
...
  • FILENAME:當前文件名
[root@Centos6 ~]#awk '{print FILENAME}' /etc/issue 

/etc/issue
/etc/issue

文件共有兩行,awk默認對所有的行進行處理,才會出現每行都顯示一次當前的文件名

  • ARGC:命令行參數的個數
[root@Centos6 ~]#awk '{print ARGC}' /etc/issue

2
2

顯示同上

  • ARGV:數組,保存命令行所給定的各參數,只有ARGV[0],ARGV[1]兩個
[root@Centos6 ~]#awk 'BEGIN{print ARGV[0],ARGV[1]}' /etc/fstab  /etc/issue

awk /etc/fstab

這個命令中,ARGV[0]保存awk,ARGV[1]保存/etc/fstab

3.2 awk自定義變量

除了awk自帶的內置變量,使用者還可以自己定義變量

自定義變量(區分字符大小寫)

(1) -v var=value

(2) 在program中直接定義

awk -v test='hello user' 'BEGIN{print test}'
awk 'BEGIN{test="hello,user";print test}'

[root@Centos6 ~]#awk -v test='hello user' 'BEGIN{print test}'
hello user

4 awk操作符

awk里面的所支持的操作符,與shall當中的操作符大同小異,這里不過的介紹,只列舉出來常見的操作符,及簡單的示例

操作符 描述
x+y, x-y, x*y, x/y, x^y, x%y,-x:轉換為負數,+x: 轉換為數值 算數操作符
=, +=, -=, *=, /=, %=, ^=,++, — 賦值操作符
==, !=, >, >=, <, <= 比較操作符
~ , !~ 模式匹配符
&&,|| 邏輯操作符
function_name (para1,para2) 函數表達式
? : 條件表達式
in 數組成員
# 模式匹配后執行action 
[root@Centos6 ~]#awk -F: '$0 ~ /root/{print $1,$3}' /etc/passwd

root 0
operator 11  
# 匹配UID為0的行,將其打印出來 
[root@Centos6 ~]#awk -F: '$3==0' /etc/passwd

root:x:0:0:root:/root:/bin/bash  
# 打印UID>=0,并且UID<=500的用戶名及ID號 
[root@Centos6 ~]#awk -F: '$3>=0 && $3<=500 {printf "%-10s %d\n",$1,$3}' /etc/passwd

root       0
bin        1
daemon     2
adm        3
...
部分省略
...
tcpdump    72
ymd        500

5 awk的數據處理


awk的輸出操作就是之前例子當中的{print …},接下來我們詳細的對其進行介紹

下面看一下awk的輸出的主要部分

  • 輸出命令
  • 數組
  • 內置函數
  • 控制語句

接下來進行詳細的解釋

(1)輸出命令

awk中提供了兩種輸出print和printf

print的使用格式:


使用格式:

print item1, item2, ...

注意:

  • 1、各項目之間使用逗號隔開,而輸出時則以空白字符分隔
  • 2、輸出的item可以為字符串或數值、當前記錄的字段(如$1)、變量或awk的表達式;數值會先轉換為字符串,而后再輸出
  • 3、print命令后面的item可以省略,此時其功能相當于print $0, 因此,如果想輸出空白行,則需要使用print ""
[root@Centos6 ~]#awk 'BEGIN { print "1\n2\n3" }'

1
2
3

[root@Centos6 ~]#awk -F: '{ print $1, $3 }' /etc/passwd

root 0
bin 1
daemon 2
adm 3
部分省略
...

這里需要說明一下: awk -F '[[:space:]:]' {action} awk中可以指定多個分隔符來進行操作,在有時候使用比較方便

printf的使用格式:


使用格式:(與C語言當中的printf命令基本相同)

printf format, item1, item2, ...

注意:

  • 1、其與print命令的最大不同是,printf需要指定(格式)format;
  • 2、format用于指定后面的每個item的輸出格式;
  • 3、printf語句不會自動打印換行符;\n

format格式的指示符都以%開頭,后跟一個字符:

  • 格式如下
%c: 顯示字符的ASCII碼  
%d, %i:十進制整數  
%e, %E:科學計數法顯示數值  
%f: 顯示浮點數  
%g, %G: 以科學計數法的格式或浮點數的格式顯示數值  
%s: 顯示字符串  
%u: 無符號整數  
%%: 顯示%自身 
修飾符:
    N: 顯示寬度;
    -: 左對齊;
    +:顯示數值符號

其中%d、%f、%s較為常用,當處理一些文本格式比較復雜時候printf,更加簡潔,美觀

上文列舉出的例子

[root@Centos6 ~]#awk -F: '{printf "%-15s %i\n",$1,$3}' /etc/passwd
root            0
bin             1
daemon          2
adm             3
...
部分省略
...
gdm             42
pulse           497
sshd            74
tcpdump         72
ymd             500

看起來很是舒服!

(2)數組

在awk內部,只支持關聯數組

其格式為:

array[index-expression]

注意: 

index-expression

  • (1) 因其是關聯數組,可以使用任意字符串表示,但要注意不要設定成,awk的內置變量
  • (2) 如果數組元素事先不存在,那么在引用其時,awk會自動創建此元素并初始化為空串

因此,要判斷某數據組中是否存在某元素,需要使用index in array的方式 要遍歷數組中的每一個元素,需要使用如下的特殊結構:    for (var in array) { statement1, … } 其中,var用于引用數組下標,而不是元素值

# 定義數組并賦值輸出 
[root@Centos6 ~]#awk 'BEGIN{test[1]="one";test[2]="two";print test[1]}'
one  
# 遍歷數組,并對出現相同匹配值計數,如下例子為統計日志中IP的訪問量 
[root@Centos6 ~]# awk '{test[$1]++}END{for(i in test){printf "%-18s %-d\n", i,test[i]}}' ./access_log

172.18.253.3       59
172.18.250.59      447
172.18.34.199      162
172.18.50.99       10
172.18.252.197     377
172.18.250.194     3
172.18.99.99       11
172.18.11.1        195
172.18.251.196     363
172.18.251.112     1044
以下省略
...  
test[$1]++  : 以每個記錄的字段為標示,并對出現相同的記錄計數 
for(i in test ): 對遍歷數組中的所有元素,并將數組中的標示賦值給i

刪除定義的數組

delete array[index]

(3)內置函數

awk也是較高大上了,當然會用到函數了,下面我們來看一下

內置數學函數:

數學函數如下表:

函數名稱 返回值
rand() 0-1之間的數
sin(x) 正弦函數
cos(x) 余弦函數
atan2(x,y) 余切函數
int(x) 取整
sqrt(x) 平方根

示例

# 隨機生成5個數乘以100,并對其結果取整
[root@Centos6 ~]# awk 'BEGIN{rand(); for (i=1;i<=5;i++)print int(rand()*100) }'

29
84
15
58
19

字符串函數

  • 1、length("s"): 指定返回字符串的長度
  • 2、sub(r,s,"t"):對t字符串進行搜索,r為匹配的內容,并將第一個匹配到的r用s替換掉
# sub替換第一次匹配的內容 
[root@Centos6 ~]#date "+%F"|awk 'sub(/-/,":",$0)'
2017:09-05 
# gsub為貪婪模式,替換所有匹配到的內容 
[root@Centos6 ~]#date "+%F"|awk 'gsub(/-/,":",$0)'
2017:09:05

3、split(s,array,"r"):以r為分隔符,對s字符串進行切割,并將切割好的字段結果保存到指定的array的數組中,第一個為array[1],第二個為[2],以此類推

[root@Centos6 ~]#date "+%F"|awk 'split($0,test,"-");END{print test[0],test[1],test[2],test[3]}'

2017-09-05
2017 09 05

自定義函數

自定義函數格式:

function name ( parameter, parameter, ... ) {
        statements
return expression
}

自定義函數,可以指定輸入的參數個數,以及返回的參數表達式

示例:

#定義一個比較大小的函數  
function max(v1,v2) {  
 v1>v2?var=v1:var=v2 return var
}

BEGIN{a=3;b=2;print max(a,b)}

(4)、控制語句:


if-else

格式:if (condition) {then-body} else {[ else-body ]}

示例:

[root@Centos6 ~]#awk -F: '{if ($3<500) {printf "%-15s  %-s\n", $1, "Admin"} else {printf "%-15s %-s\n", $1, "Common User"}}' /etc/passwd
root             Admin
bin              Admin
daemon           Admin
adm              Admin
lp               Admin
...
部分省略
...
ymd             Common User
部分省略
...

注意

單條語句{}花括號可不加,但';'分號要加

while

格式: while (condition){statement1; statment2; …}

[root@Centos6 ~]#awk -F: '{i=1;while (i<=NF) { if (length($i)<=5) {print $i}; i++ }}' /etc/passwd
root
x
0
0
root
/root
bin
以下省略
...

do-while

格式: do {statement1, statement2, …} while (condition)

# 計算1-100的和  
[root@Centos6 ~]#awk 'BEGIN{i=1;do{sum+=i;i++}while(i<=100){print sum}}' 

5050

for

格式: for ( variable assignment; condition; iteration process) { statement1, statement2, …}

# 1-100的和 
[root@Centos6 ~]#awk 'BEGIN{for(i=0;i<=100;i++)sum+=i;print sum}'
5050

for循環還可以用來遍歷數組元素:

格式: for (i in array) {statement1, statement2, …}

# 統計/etc/passwd文件中的bash類型
[root@Centos6 ~]#awk -F: '$NF!~/^$/{BASH[$NF]++}END{for(A in BASH){printf "%15s:%i\n",A,BASH[A]}}' /etc/passwd
 /sbin/shutdown:1
      /bin/bash:3
  /sbin/nologin:29
     /sbin/halt:1
      /bin/sync:1

case

格式: switch (expression) { case VALUE or /REGEXP/: statement1, statement2,… default: statement1, …}

break 和 continue

常用于循環或case語句中,含義及用法與shell中的相同,這里不再贅述

next

提前結束對本行文本的處理,并接著處理下一行; 例如,下面的命令將顯示其ID號為奇數的用戶:

# 統計ID為奇數的用戶
[root@Centos6 ~]#awk -F: '{if($3%2==0) next;printf "%-10s %d\n", $1,$3}' /etc/passwd

bin        1
adm        3
sync       5
halt       7
operator   11
gopher     13
nobody     99
dbus       81
usbmuxd    113
rtkit      499
vcsa       69
abrt       173
rpcuser    29
postfix    89
mysql      27
pulse      497

以上就是awk的全部內容,由于本片文章是基于部分操作寫成的,難免有不足及錯誤的地方,敬請諒解!

原創文章,作者:M25_ymd,如若轉載,請注明出處:http://www.www58058.com/86347

(0)
M25_ymdM25_ymd
上一篇 2017-09-05 21:50
下一篇 2017-09-07 15:10

相關推薦

  • 文件系統知一二

    文件系統知一二 What?文件系統是什么?     我們經常說文件系統,那么文件系統是什么東西呢?    計算機的文件系統是一種存儲和組織計算機數據的組織結構。 常見的文件系統有哪些?           &n…

    Linux干貨 2016-03-09
  • 馬哥教育網絡班21期+第6周課程練習

    請詳細總結vim編輯器的使用并完成以下練習題1、復制/etc/rc.d/rc.sysinit文件至/tmp目錄,將/tmp/rc.sysinit文件中的以至少一個空白字符開頭的行的行首加#; %s/^([[:space:]]{1,}.*)/#\1/s 2、復制/boot/grub/grub.conf至/tmp目錄中,刪除/tmp/grub.conf文件中的行…

    Linux干貨 2016-08-10
  • 軟Raid

    軟RAID     一.概念 RAID是多個磁盤合成一個“陣列”來提供更好的性能、冗余、或者兩者都提供。 二.作用 提高IO能力: 磁盤并行讀寫 提高冗余來實現 :磁盤冗余來實現 三.實現方式 外接式磁盤陣列:通過擴展卡提供適配能力 內接式RAID:主板集成RAID控制器 軟件RAID:通過OS實現(今天的重點) 四.RAID級別 1. …

    2017-08-29
  • httpd服務歸納:httpd基本配置(周邊常用工具,httpd 編譯安裝)

    一、 服務器status頁     內生的status信息,可以通過web予以顯示, 可以映射為url地址進行訪問     1. 配置路徑有幾種不同方式          如果URL可以映射…

    Linux干貨 2015-05-27
  • keepalive

    HA Cluster: 集群類型:LB(lvs/nginx(http/upstream, stream/upstream))、HA、HP SPoF: Single Point of Failure 系統可用性的公式:A=MTBF/(MTBF+MTTR)(0,1), 95%幾個9(指標): 99%, …, 99.999%,99.9999%;99%:…

    Linux干貨 2017-05-22
  • Linux系統shell腳本

                                          &n…

    系統運維 2016-09-01

評論列表(1條)

  • N27_dxldeng
    N27_dxldeng 2017-09-07 15:06

    謝謝分享

欧美性久久久久