溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

第七章 Shell文本處理三劍客之a(chǎn)wk

發(fā)布時間:2020-06-30 16:21:25 來源:網(wǎng)絡(luò) 閱讀:18656 作者:李振良OK 欄目:開發(fā)技術(shù)

上節(jié)講了grep、sed工具,已經(jīng)能滿足常見的文本處理需求,但有些需求對于他們來說心有余而力不足,今天所講的工具就能完成他們大多數(shù)的功能,它就是三劍客中的老大AWK,我相信一定不會讓你失望,下面一起看看吧!

本章大綱:

第七章  Shell文本處理三劍客之a(chǎn)wk

8.3 awk

awk是一個處理文本的編程語言工具,能用簡短的程序處理標準輸入或文件、數(shù)據(jù)排序、計算以及生成報表等等。

在Linux系統(tǒng)下默認awk是gawk,它是awk的GNU版本。可以通過命令查看應(yīng)用的版本:ls -l /bin/awk

基本的命令語法:awk option 'pattern {action}' file

其中pattern表示AWK在數(shù)據(jù)中查找的內(nèi)容,而action是在找到匹配內(nèi)容時所執(zhí)行的一系列命令?;ɡㄌ栍糜诟鶕?jù)特定的模式對一系列指令進行分組。

awk處理的工作方式與數(shù)據(jù)庫類似,支持對記錄和字段處理,這也是grep和sed不能實現(xiàn)的。

在awk中,缺省的情況下將文本文件中的一行視為一個記錄,逐行放到內(nèi)存中處理,而將一行中的某一部分作為記錄中的一個字段。用1,2,3...數(shù)字的方式順序的表示行(記錄)中的不同字段。用$后跟數(shù)字,引用對應(yīng)的字段,以逗號分隔,0表示整個行。

8.3.1 選項

選項

描述

-f program-file

從文件中讀取awk程序源文件

-F fs

指定fs為輸入字段分隔符

-v var=value

變量賦值

--posix

兼容POSIX正則表達式

--dump-variables=[file]

把awk命令時的全局變量寫入文件,

默認文件是awkvars.out

--profile=[file]

格式化awk語句到文件,默認是awkprof.out

8.3.2 模式

常用模式有:

Pattern

Description

BEGIN{ }

給程序賦予初始狀態(tài),先執(zhí)行的工作

END{ }

程序結(jié)束之后執(zhí)行的一些掃尾工作

/regular  expression/

為每個輸入記錄匹配正則表達式

pattern &&  pattern

邏輯and,滿足兩個模式

pattern || pattern

邏輯or,滿足其中一個模式

! pattern

邏輯not,不滿足模式

pattern1, pattern2

范圍模式,匹配所有模式1的記錄,直到匹配到模式2

而動作呢,就是下面所講的print、流程控制、I/O語句等。

示例:

1)從文件讀取awk程序處理文件

# vi test.awk
{print$2}
# tail -n3 /etc/services |awk -f test.awk
48049/tcp
48128/tcp
49000/tcp

2)指定分隔符,打印指定字段 

打印第二字段,默認以空格分隔:
# tail -n3 /etc/services |awk '{print $2}'
48049/tcp
48128/tcp
48128/udp
指定冒號為分隔符打印第一字段:
# awk-F ':' '{print $1}' /etc/passwd
root
bin
daemon
adm
lp
sync
......

還可以指定多個分隔符,作為同一個分隔符處理:

# tail -n3 /etc/services |awk -F'[/#]' '{print $3}'   
 iqobject
 iqobject
 MatahariBroker
# tail -n3 /etc/services |awk -F'[/#]' '{print $1}'
iqobject       48619
iqobject       48619
matahari       49000
# tail -n3 /etc/services |awk -F'[/#]' '{print $2}'
tcp              
udp              
tcp              
# tail -n3 /etc/services |awk -F'[/#]' '{print $3}'
 iqobject
 iqobject
 MatahariBroker
# tail -n3 /etc/services |awk -F'[ /]+' '{print $2}'
48619
48619
49000

[]元字符的意思是符號其中任意一個字符,也就是說每遇到一個/或#時就分隔一個字段,當用多個分隔符時,就能更方面處理字段了。

3)變量賦值

# awk-v a=123 'BEGIN{print a}'   
123
系統(tǒng)變量作為awk變量的值:
#a=123
# awk-v a=$a 'BEGIN{print a}'   
123
或使用單引號
# awk'BEGIN{print '$a'}'   
123

4)輸出awk全局變量到文件

# seq 5|awk --dump-variables '{print $0}'
1
2
3
4
5
# cat awkvars.out                         
ARGC:number (1)
ARGIND:number (0)
ARGV:array, 1 elements
BINMODE:number (0)
CONVFMT:string ("%.6g")
ERRNO:number (0)
FIELDWIDTHS:string ("")
FILENAME:string ("-")
FNR:number (5)
FS:string (" ")
IGNORECASE:number (0)
LINT:number (0)
NF:number (1)
NR:number (5)
OFMT:string ("%.6g")
OFS:string (" ")
ORS:string ("\n")
RLENGTH:number (0)
RS:string ("\n")
RSTART:number (0)
RT:string ("\n")
SUBSEP:string ("\034")
TEXTDOMAIN:string ("messages")

5)BEGIN和END

BEGIN模式是在處理文件之前執(zhí)行該操作,常用于修改內(nèi)置變量、變量賦值和打印輸出的頁眉或標題。

例如:打印頁眉

# tail /etc/services |awk 'BEGIN{print"Service\t\tPort\t\t\tDescription\n==="}{print $0}'
Service        Port                   Description
===
3gpp-cbsp      48049/tcp              # 3GPP Cell Broadcast Service 
isnetserv      48128/tcp              # Image Systems Network Services
isnetserv      48128/udp              # Image Systems Network Services
blp5          48129/tcp              # Bloomberg locator
blp5          48129/udp              # Bloomberg locator
com-bardac-dw    48556/tcp              # com-bardac-dw
com-bardac-dw    48556/udp              # com-bardac-dw
iqobject       48619/tcp               #iqobject
iqobject       48619/udp              # iqobject
matahari       49000/tcp              # Matahari Broker

END模式是在程序處理完才會執(zhí)行。

例如:打印頁尾

# tail /etc/services |awk '{print $0}END{print "===\nEND......"}'
3gpp-cbsp      48049/tcp              # 3GPP Cell Broadcast Service 
isnetserv      48128/tcp              # Image Systems Network Services
isnetserv      48128/udp              # Image Systems Network Services
blp5          48129/tcp              # Bloomberg locator
blp5          48129/udp              # Bloomberg locator
com-bardac-dw    48556/tcp              # com-bardac-dw
com-bardac-dw    48556/udp              # com-bardac-dw
iqobject       48619/tcp              # iqobject
iqobject       48619/udp              # iqobject
matahari       49000/tcp              # Matahari Broker
===
END......

6)格式化輸出awk命令到文件

# tail /etc/services |awk --profile 'BEGIN{print"Service\t\tPort\t\t\tDescription\n==="}{print $0}END{print"===\nEND......"}'
Service         Port                    Description
===
nimgtw          48003/udp               # Nimbus Gateway
3gpp-cbsp       48049/tcp               # 3GPP Cell Broadcast ServiceProtocol
isnetserv       48128/tcp               # Image Systems Network Services
isnetserv       48128/udp               # Image Systems Network Services
blp5            48129/tcp               # Bloomberg locator
blp5            48129/udp               # Bloomberg locator
com-bardac-dw   48556/tcp               # com-bardac-dw
com-bardac-dw   48556/udp               # com-bardac-dw
iqobject        48619/tcp               # iqobject
iqobject        48619/udp               # iqobject
===
END......
# cat awkprof.out 
        # gawk profile, created Sat Jan  7 19:45:22 2017
 
        # BEGIN block(s)
 
        BEGIN {
                print"Service\t\tPort\t\t\tDescription\n==="
        }
 
        # Rule(s)
 
        {
                print $0
        }
 
        # END block(s)
 
        END {
                print "===\nEND......"
        }

7)/re/正則匹配

匹配包含tcp的行:
# tail /etc/services |awk '/tcp/{print $0}'   
3gpp-cbsp      48049/tcp              # 3GPP Cell Broadcast Service 
isnetserv      48128/tcp              # Image Systems Network Services
blp5          48129/tcp              # Bloomberg locator
com-bardac-dw    48556/tcp              # com-bardac-dw
iqobject       48619/tcp              # iqobject
matahari       49000/tcp              # Matahari Broker
匹配開頭是blp5的行:
# tail /etc/services |awk '/^blp5/{print $0}'   
blp5          48129/tcp              # Bloomberg locator
blp5          48129/udp              # Bloomberg locator
匹配第一個字段是8個字符的行:
# tail /etc/services |awk '/^[a-z0-9]{8} /{print $0}'
iqobject       48619/tcp              # iqobject
iqobject       48619/udp              # iqobject
matahari       49000/tcp              # Matahari Broker

8)邏輯and、or和not

匹配記錄中包含blp5和tcp的行:
#tail /etc/services |awk '/blp5/ && /tcp/{print $0}'      
blp5          48129/tcp              # Bloomberg locator
匹配記錄中包含blp5或tcp的行:
#tail /etc/services |awk '/blp5/ || /tcp/{print $0}'       
3gpp-cbsp      48049/tcp              # 3GPP Cell Broadcast Service 
isnetserv      48128/tcp              # Image Systems Network Services
blp5          48129/tcp              # Bloomberg locator
blp5          48129/udp              # Bloomberg locator
com-bardac-dw    48556/tcp              # com-bardac-dw
iqobject       48619/tcp              # iqobject
matahari       49000/tcp              # Matahari Broker
不匹配開頭是#和空行:
# awk'! /^#/ && ! /^$/{print $0}' /etc/httpd/conf/httpd.conf
或
# awk'! /^#|^$/' /etc/httpd/conf/httpd.conf  
或
# awk'/^[^#]|"^$"/' /etc/httpd/conf/httpd.conf

9)匹配范圍

# tail /etc/services |awk '/^blp5/,/^com/'
blp5           48129/tcp              # Bloomberg locator
blp5           48129/udp              # Bloomberg locator
com-bardac-dw      48556/tcp              # com-bardac-dw

博客地址:http://lizhenliang.blog.51cto.com

QQ群:323779636(Shell/Python運維開發(fā)群)

8.3.3 內(nèi)置變量

變量名

描述

FS

輸入字段分隔符,默認是空格或制表符

OFS

輸出字段分隔符,默認是空格

RS

輸入記錄分隔符,默認是換行符\n

ORS

輸出記錄分隔符,默認是換行符\n

NF

統(tǒng)計當前記錄中字段個數(shù)

NR

統(tǒng)計記錄編號,每處理一行記錄,編號就會+1

FNR

統(tǒng)計記錄編號,每處理一行記錄,編號也會+1,與NR不同的是,處理第二個文件時,編號會重新計數(shù)。

ARGC

命令行參數(shù)數(shù)量

ARGIND

當前正在處理的文件索引值。第一個文件是1,第二個文件是2,以此類推

ARGV

命令行參數(shù)數(shù)組序列數(shù)組,下標從0開始,ARGV[0]是awk

ENVIRON

當前系統(tǒng)的環(huán)境變量

FILENAME

輸出當前處理的文件名

IGNORECASE

忽略大小寫

SUBSEP

數(shù)組中下標的分隔符,默認為"\034"

示例:

1)FS和OFS

在程序開始前重新賦值FS變量,改變默認分隔符為冒號,與-F一樣。

# awk 'BEGIN{FS=":"}{print $1,$2}' /etc/passwd |head -n5          
rootx
bin x
daemonx
adm x
lp x
也可以使用-v來重新賦值這個變量:
# awk -vFS=':' '{print $1,$2}' /etc/passwd |head -n5      # 中間逗號被換成了OFS的默認值        
rootx
bin x
daemonx
adm x
lp x
由于OFS默認以空格分隔,反向引用多個字段分隔的也是空格,如果想指定輸出分隔符這樣:
# awk 'BEGIN{FS=":";OFS=":"}{print $1,$2}' /etc/passwd |head -n5
root:x
bin:x
daemon:x
adm:x
lp:x
也可以通過字符串拼接實現(xiàn)分隔:
# awk 'BEGIN{FS=":"}{print $1"#"$2}' /etc/passwd |head -n5
root#x
bin#x
daemon#x
adm#x
lp#x

2)RS和ORS

RS默認是\n分隔每行,如果想指定以某個字符作為分隔符來處理記錄:

# echo "www.baidu.com/user/test.html" |awk'BEGIN{RS="/"}{print $0}'
www.baidu.com
user
test.html
 
RS也支持正則,簡單演示下:
# seq-f "str%02g" 10 |sed 'n;n;a\-----' |awk 'BEGIN{RS="-+"}{print$1}'
str01
str04
str07
str10
將輸出的換行符替換為+號:
# seq10 |awk 'BEGIN{ORS="+"}{print $0}'
1+2+3+4+5+6+7+8+9+10+
替換某個字符:
#tail -n2 /etc/services |awk 'BEGIN{RS="/";ORS="#"}{print$0}'
iqobject       48619#udp              # iqobject
matahari       49000#tcp              # Matahari Broker

3)NF

NF是打印字段個數(shù)。

# echo "a b c d e f" |awk '{print NF}'
6
打印最后一個字段:
# echo "a b c d e f" |awk '{print $NF}'
f
打印倒數(shù)第二個字段:
# echo "a b c d e f" |awk '{print $(NF-1)}'
e
排除最后兩個字段:
# echo "a b c d e f" |awk '{$NF="";$(NF-1)="";print$0}'
a b cd
排除第一個字段:
# echo "a b c d e f" |awk '{$1="";print $0}'
 bc d e f

4)NR和FNR

NR統(tǒng)計記錄編號,每處理一行記錄,編號就會+1,F(xiàn)NR不同的是在統(tǒng)計第二個文件時會重新計數(shù)。

打印行數(shù):
# tail -n5 /etc/services |awk '{print NR,$0}'
1 com-bardac-dw     48556/tcp              # com-bardac-dw
2 com-bardac-dw     48556/udp              # com-bardac-dw
3 iqobject        48619/tcp              # iqobject
4 iqobject        48619/udp              # iqobject
5 matahari        49000/tcp              # Matahari Broker
打印總行數(shù):
# tail -n5 /etc/services |awk 'END{print NR}'
5
打印第三行:
# tail -n5 /etc/services |awk 'NR==3'       
iqobject       48619/tcp              # iqobject
打印第三行第二個字段:
# tail -n5 /etc/services |awk 'NR==3{print $2}'
48619/tcp
打印前三行:
# tail -n5 /etc/services |awk 'NR<=3{print NR,$0}'
1 com-bardac-dw   48556/tcp              # com-bardac-dw
2 com-bardac-dw   48556/udp              # com-bardac-dw
3 iqobject       48619/tcp              # iqobject

看下NR和FNR的區(qū)別:

# cat a
a
b
c
# cat b
c
d
e
# awk'{print NR,FNR,$0}' a b
1 1 a
2 2 b
3 3 c
4 1 c
5 2 d
6 3 e

可以看出NR每處理一行就會+1,而FNR在處理第二個文件時,編號重新計數(shù)。同時也知道awk處理兩個文件時,是合并到一起處理。

# awk 'FNR==NR{print $0"1"}FNR!=NR{print $0"2"}' a b 
a1
b1
c1
c2
d2
e2

當FNR==NR時,說明在處理第一個文件內(nèi)容,不等于時說明在處理第二個文件內(nèi)容。

一般FNR在處理多個文件時會用到,下面會講解。

5)ARGC和ARGV

ARGC是命令行參數(shù)數(shù)量

ARGV是將命令行參數(shù)存到數(shù)組,元素由ARGC指定,數(shù)組下標從0開始

# awk 'BEGIN{print ARGC}' 1 2 3
4
# awk 'BEGIN{print ARGV[0]}'
awk
# awk 'BEGIN{print ARGV[1]}' 1 2
1
# awk 'BEGIN{print ARGV[2]}' 1 2 
2

6)ARGIND

ARGIND是當前正在處理的文件索引值,第一個文件是1,第二個文件是2,以此類推,從而可以通過這種方式判斷正在處理哪個文件。

# awk '{print ARGIND,$0}' a b
1 a
1 b
1 c
2 c
2 d
2 e
# awk 'ARGIND==1{print "a->"$0}ARGIND==2{print "b->"$0}'a  b       
a->a
a->b
a->c
b->c
b->d
b->e

7)ENVIRON

ENVIRON調(diào)用系統(tǒng)變量。

# awk 'BEGIN{print ENVIRON["HOME"]}'
/root
如果是設(shè)置的環(huán)境變量,還需要用export導(dǎo)入到系統(tǒng)變量才可以調(diào)用:
# awk'BEGIN{print ENVIRON["a"]}'
 
# export a
# awk 'BEGIN{print ENVIRON["a"]}'
123

8)FILENAME

FILENAME是當前處理文件的文件名。

# awk 'FNR==NR{print FILENAME"->"$0}FNR!=NR{printFILENAME"->"$0}' a b     
a->a
a->b
a->c
b->c
b->d
b->e
9)忽略大小寫
# echo "A a b c" |xargs -n1 |awk 'BEGIN{IGNORECASE=1}/a/'
A
a

等于1代表忽略大小寫。


博客地址:http://lizhenliang.blog.51cto.com

QQ群:323779636(Shell/Python運維開發(fā)群)

8.3.4 操作符

運算符

描述

(....)

分組

$

字段引用

++ --

遞增和遞減

+ - !

加號,減號,和邏輯否定

* / %

乘,除和取余

+ -

加法,減法

| |&

管道,用于getline,print和printf

< > <=  >= != ==

關(guān)系運算符

~ !~

正則表達式匹配,否定正則表達式匹配

in

數(shù)組成員

&& ||

邏輯and,邏輯or

?:

簡寫條件表達式:

expr1 ? expr2 :  expr3

第一個表達式為真,執(zhí)行expr2,否則執(zhí)行expr3

= += -= *= /= %= ^=

變量賦值運算符

須知:在awk中,有3種情況表達式為假:數(shù)字是0,空字符串和未定義的值

數(shù)值運算,未定義變量初始值為0。字符運算,未定義變量初始值為空。

舉例測試:

# awk 'BEGIN{n=0;if(n)print"true";else print "false"}'
false
# awk'BEGIN{s="";if(s)print "true";else print"false"}'
false
# awk'BEGIN{if(s)print "true";else print "false"}'
false

示例:

1)截取整數(shù)

# echo "123abc abc123 123abc123"|xargs -n1 | awk '{print +$0}'
123
0
123
# echo "123abc abc123 123abc123"|xargs -n1 | awk '{print -$0}'
-123
0
-123

2)感嘆號

打印奇數(shù)行:
# seq 6 |awk 'i=!i'
1
3
5
讀取第一行,i是未定義變量,也就是i=!0,!取反意思。感嘆號右邊是個布爾值,0或空字符串為假,非0或非空字符串為真,!0就是真,因此i=1,條件為真打印當前記錄。
沒有print為什么會打印呢?因為模式后面沒有動作,默認會打印整條記錄。
讀取第二行,因為上次i的值由0變成了1,此時就是i=!1,條件為假不打印。
讀取第三行,上次條件又為假,i恢復(fù)初始值0,取反,繼續(xù)打印。以此類推...
可以看出,運算時并沒有判斷行內(nèi)容,而是利用布爾值真假判斷輸出當前行。
打印偶數(shù)行:
# seq 6 |awk '!(i=!i)'   
2
4
6

2)不匹配某行

# tail /etc/services |awk '!/blp5/{print$0}'
3gpp-cbsp      48049/tcp               # 3GPPCell Broadcast Service isnetserv       48128/tcp              # Image Systems NetworkServices
isnetserv      48128/udp               # ImageSystems Network Services
com-bardac-dw     48556/tcp              # com-bardac-dw
com-bardac-dw     48556/udp              # com-bardac-dw
iqobject       48619/tcp               # iqobject
iqobject       48619/udp               # iqobject
matahari       49000/tcp               # MatahariBroker

3)乘法和除法

# seq 5 |awk '{print $0*2}'
2
4
6
8
10
# seq 5 |awk '{print $0%2}'
1
0
1
0
1
打印偶數(shù)行:
# seq 5 |awk '$0%2==0{print $0}'
2
4
打印奇數(shù)行:
# seq 5 |awk '$0%2!=0{print $0}'
1
3
5

4)管道符使用

# seq 5 |shuf |awk '{print$0|"sort"}'
1
2
3
4
5

5)正則表達式匹配

# seq 5 |awk '$0~3{print $0}'
3
# seq 5 |awk '$0!~3{print $0}'
1
2
4
5
# seq 5 |awk '$0~/[34]/{print $0}'
3
4
# seq 5 |awk '$0!~/[34]/{print $0}'
1
2
5
# seq 5 |awk '$0~/[^34]/{print $0}'
1
2
5

6)判斷數(shù)組成員

# awk'BEGIN{a["a"]=123}END{if("a" in a)print "yes"}'</dev/null
yes

7)三目運算符

# awk 'BEGIN{print1==1?"yes":"no"}'  # 三目運算作為一個表達式,里面不允許寫print
yes
# seq 3 |awk '{print$0==2?"yes":"no"}'
no
yes
no
替換換行符為逗號:
# seq 5 |awk '{printn=(n?n","$0:$0)}'
1
1,2
1,2,3
1,2,3,4
1,2,3,4,5
# seq 5 |awk'{n=(n?n","$0:$0)}END{print n}'
1,2,3,4,5
說明:讀取第一行時,n沒有變量,為假輸出$0也就是1,并賦值變量n,讀取第二行時,n是1為真,輸出1,2 以此類推,后面會一直為真。
每三行后面添加新一行:
# seq 10 |awk '{print NR%3?$0:$0"\ntxt"}'
1
2
3
txt
4
5
6
txt
7
8
9
txt
10
在
兩行合并一行:
# seq 6 |awk '{printf NR%2!=0?$0"":$0" \n"}' 
1 2
3 4
5 6
# seq 6 |awk 'ORS=NR%2?"":"\n"'
1 2
3 4
5 6
# seq 6 |awk '{if(NR%2)ORS="";else ORS="\n";print}'

8)變量賦值

字段求和:
# seq 5 |awk '{sum+=1}END{print sum}'
5
# seq 5 |awk '{sum+=$0}END{print sum}'
15

8.3.5 流程控制

1)if語句

格式:if(condition) statement [ else statement ]

單分支:
# seq5 |awk '{if($0==3)print $0}'   
3
雙分支:
# seq5 |awk '{if($0==3)print $0;else print "no"}'
no
no
3
no
no
多分支:
# catfile
1 2 3
4 5 6
7 8 9
# awk'{if($1==4){print "1"} else if($2==5){print "2"} elseif($3==6){print "3"} else {print "no"}}' file          
no
1
no

2)while語句

格式:while(condition) statement

遍歷打印所有字段:
# awk'{i=1;while(i<=NF){print $i;i++}}' file
1
2
3
4
5
6
7
8
9
awk是按行處理的,每次讀取一行,并遍歷打印每個字段。

3)for語句C語言風(fēng)格

格式:for(expr1; expr2; expr3) statement

遍歷打印所有字段:
# catfile
1 2 3
4 5 6
7 8 9
# awk'{for(i=1;i<=NF;i++)print $i}' file
1
2
3
4
5
6
7
8
9
倒敘打印文本:
# awk'{for(i=NF;i>=1;i--)print $i}' file       
3
2
1
6
5
4
9
8
7
都換行了,這并不是我們要的結(jié)果。怎么改進呢?
# awk'{for(i=NF;i>=1;i--){printf $i" "};print ""}' file # print本身就會新打印一行
3 2 1
6 5 4
9 8 7
或
# awk'{for(i=NF;i>=1;i--)if(i==1)printf $i"\n";else printf $i""}' file
3 2 1
6 5 4
6 5 4
9 8 7
在這種情況下,是不是就排除第一行和倒數(shù)第一行呢?我們正序打印看下
排除第一行:
# awk'{for(i=2;i<=NF;i++){printf $i" "};print ""}' file
2 3
5 6
8 9
排除第二行:
# awk'{for(i=1;i<=NF-1;i++){printf $i" "};print ""}' file
1 2
4 5
7 8
IP加單引號:
#echo '10.10.10.1 10.10.10.2 10.10.10.3' |awk '{for(i=1;i<=NF;i++)printf"\047"$i"\047"}
'10.10.10.1' '10.10.10.2'  '10.10.10.3'
\047是ASCII碼,可以通過showkey -a命令查看。
4)for語句遍歷數(shù)組
格式:for(var in array) statement
# seq-f "str%.g" 5 |awk '{a[NR]=$0}END{for(v in a)print v,a[v]}'
4 str4
5 str5
1 str1
2 str2
3 str3

5)break和continue語句

break跳過所有循環(huán),continue跳過當前循環(huán)。

# awk 'BEGIN{for(i=1;i<=5;i++){if(i==3){break};print i}}'
1
2
# awk 'BEGIN{for(i=1;i<=5;i++){if(i==3){continue};print i}}'
1
2
4
5

6)刪除數(shù)組和元素

格式:

deletearray[index]  刪除數(shù)組元素

deletearray  刪除數(shù)組

# seq-f "str%.g" 5 |awk '{a[NR]=$0}END{delete a;for(v in a)print v,a[v]}'
空的…  
# seq-f "str%.g" 5 |awk '{a[NR]=$0}END{delete a[3];for(v in a)printv,a[v]}'
4 str4
5 str5
1 str1
2 str2

7)exit語句

格式:exit[ expression ]

exit退出程序,與shell的exit一樣。[ expr]是0-255之間的數(shù)字。

# seq5 |awk '{if($0~/3/)exit (123)}'         
# echo $?
123

8.3.6 數(shù)組

數(shù)組是用來存儲一系列值的變量,通過下標(索引)來訪問值。

awk中數(shù)組稱為關(guān)聯(lián)數(shù)組,不僅可以使用數(shù)字作為下標,還可以使用字符串作為下標。

數(shù)組元素的鍵和值存儲在awk程序內(nèi)部的一個表中,該表采用散列算法,因此數(shù)組元素是隨機排序。

數(shù)組格式:array[index]=value

1)自定義數(shù)組

# awk 'BEGIN{a[0]="test";print a[0]}'
test

2)通過NR設(shè)置記錄下標,下標從1開始

# tail -n3 /etc/passwd |awk -F: '{a[NR]=$1}END{print a[1]}'
systemd-network
# tail -n3 /etc/passwd |awk -F: '{a[NR]=$1}END{print a[2]}'
zabbix
# tail -n3 /etc/passwd |awk -F: '{a[NR]=$1}END{print a[3]}'
user

3)通過for循環(huán)遍歷數(shù)組

# tail -n5 /etc/passwd |awk -F: '{a[NR]=$1}END{for(v in a)print a[v],v}'
zabbix4
user5
admin1
systemd-bus-proxy2
systemd-network3
# tail -n5 /etc/passwd |awk -F: '{a[NR]=$1}END{for(i=1;i<=NR;i++)printa[i],i}'
admin1
systemd-bus-proxy2
systemd-network3
zabbix4
user5

上面打印的i是數(shù)組的下標。

第一種for循環(huán)的結(jié)果是亂序的,剛說過,數(shù)組是無序存儲。

第二種for循環(huán)通過下標獲取的情況是排序正常。

所以當下標是數(shù)字序列時,還是用for(expr1;expr2;expr3)循環(huán)表達式比較好,保持順序不變。

4)通過++方式作為下標

# tail -n5 /etc/passwd |awk -F: '{a[x++]=$1}END{for(i=0;i<=x-1;i++)printa[i],i}'
admin0
systemd-bus-proxy1
systemd-network2
zabbix3
user4

x被awk初始化值是0,沒循環(huán)一次+1

5)使用字段作為下標

# tail -n5 /etc/passwd |awk -F: '{a[$1]=$7}END{for(v in a)print a[v],v}'
/sbin/nologinadmin
/bin/bashuser
/sbin/nologinsystemd-network
/sbin/nologinsystemd-bus-proxy
/sbin/nologinzabbix

6)統(tǒng)計相同字段出現(xiàn)次數(shù)

# tail /etc/services |awk '{a[$1]++}END{for(v in a)print a[v],v}'
2com-bardac-dw
13gpp-cbsp
2iqobject
1matahari
2isnetserv
2blp5
# tail /etc/services |awk '{a[$1]+=1}END{for(v in a)print a[v],v}' 
2com-bardac-dw
13gpp-cbsp
2iqobject
1matahari
2isnetserv
2blp5
#tail /etc/services |awk '/blp5/{a[$1]++}END{for(v in a)print a[v],v}'
2blp5

第一個字段作為下標,值被++初始化是0,每次遇到下標(第一個字段)一樣時,對應(yīng)的值就會被+1,因此實現(xiàn)了統(tǒng)計出現(xiàn)次數(shù)。

想要實現(xiàn)去重的的話就簡單了,只要打印下標即可。

7)統(tǒng)計TCP連接狀態(tài)

# netstat -antp |awk '/^tcp/{a[$6]++}END{for(v in a)print a[v],v}'
9LISTEN
6ESTABLISHED
6TIME_WAIT

8)只打印出現(xiàn)次數(shù)大于等于2的

# tail /etc/services |awk '{a[$1]++}END{for(v in a) if(a[v]>=2){printa[v],v}}'
2com-bardac-dw
2iqobject
2isnetserv
2blp5

9)去重

只打印重復(fù)的行:
# tail /etc/services |awk 'a[$1]++'
isnetserv      48128/udp              # Image Systems Network Services
blp5          48129/udp              # Bloomberg locator
com-bardac-dw    48556/udp              # com-bardac-dw
iqobject       48619/udp              # iqobject
去重:
# tail /etc/services |awk '!a[$1]++'
3gpp-cbsp      48049/tcp              # 3GPP Cell Broadcast Service 
isnetserv      48128/tcp              # Image Systems Network Services
blp5          48129/tcp              # Bloomberg locator
com-bardac-dw    48556/tcp              # com-bardac-dw
iqobject       48619/tcp              # iqobject
matahari       49000/tcp              # Matahari Broker

只打印重復(fù)的行說明:先明白一個情況,當值是0是為假,1為真,知道這點就不難理解了。由于執(zhí)行了++當處理第一條記錄時,初始值是0為假,就不打印,如果再遇到相同的記錄,值就會+1,不為0,打印。

去重說明:初始值是0為假,感嘆號取反為真,打印,也就是說,每個記錄的第一個值都是為0,所以都會打印,如果再遇到相同的記錄+1,值就會為真,取反為假就不打印。

# tail /etc/services |awk '{if(a[$1]++)print $1}'      
isnetserv
blp5
com-bardac-dw
iqobject
使用三目運算:
# tail /etc/services |awk '{print a[$1]++?$1:"no"}'   
no
no
isnetserv
no
blp5
no
com-bardac-dw
no
iqobject
no
# tail /etc/services |awk '{if(!a[$1]++)print $1}'
3gpp-cbsp
isnetserv
blp5
com-bardac-dw
iqobject
matahari

10)統(tǒng)計每個相同字段的某字段總數(shù):

# tail /etc/services |awk -F'[ /]+' '{a[$1]+=$2}END{for(v in a)print v, a[v]}'
com-bardac-dw97112
3gpp-cbsp48049
iqobject97238
matahari49000
isnetserv96256
blp596258

11)多維數(shù)組

awk的多維數(shù)組,實際上awk并不支持多維數(shù)組,而是邏輯上模擬二維數(shù)組的訪問方式,比如a[a,b]=1,使用SUBSEP(默認\034)作為分隔下標字段,存儲后是這樣a\034b。

示例:

# awk 'BEGIN{a["x","y"]=123;for(v in a) print v,a[v]}'
xy123
我們可以重新復(fù)制SUBSEP變量,改變下標默認分隔符:
# awk 'BEGIN{SUBSEP=":";a["x","y"]=123;for(v in a)print v,a[v]}'
x:y123
根據(jù)指定的字段統(tǒng)計出現(xiàn)次數(shù):
# cata
A 192.168.1.1 HTTP
B 192.168.1.2 HTTP
B 192.168.1.2 MYSQL
C 192.168.1.1 MYSQL
C 192.168.1.1 MQ
D 192.168.1.4 NGINX
# awk 'BEGIN{SUBSEP="-"}{a[$1,$2]++}END{for(v in a)print a[v],v}' a
1 D-192.168.1.4
1 A-192.168.1.1
2 C-192.168.1.1
2 B-192.168.1.2

博客地址:http://lizhenliang.blog.51cto.com

QQ群:323779636(Shell/Python運維開發(fā)群)

8.3.7 內(nèi)置函數(shù)

函數(shù)

描述

int(expr)

截斷為整數(shù)

sqrt(expr)

平方根

rand()

返回一個隨機數(shù)N,0和1范圍,0 < N < 1

srand([expr])

使用expr生成隨機數(shù),如果不指定,默認使用當前時間為種子,如果前面有種子則使用生成隨機數(shù) 

asort(a, b)

對數(shù)組a的值進行排序,把排序后的值存到新的數(shù)組b中,新排序的數(shù)組下標從1開始

asorti(a,b)

對數(shù)組a的下標進行排序,同上

sub(r, s [, t])

對輸入的記錄用s替換r,t可選針對某字段替換 ,但只替換第一個字符串

gsub(r,s [, t])

對輸入的記錄用s替換r,t可選針對某字段替換,替換所有字符串

index(s, t)

返回s中字符串t的索引位置,0為不存在

length([s])

返回s的長度

match(s, r [, a])

測試字符串s是否包含匹配r的字符串

split(s, a [, r  [, seps] ])

根據(jù)分隔符seps將s分成數(shù)組a

substr(s, i [,  n])

截取字符串s從i開始到長度n,如果n沒指定則是剩余部分

tolower(str)

str中的所有大寫轉(zhuǎn)換成小寫

toupper(str)

str中的所有小寫轉(zhuǎn)換成大寫

systime()

當前時間戳

strftime([format  [, timestamp[, utc-flag]]])

格式化輸出時間,將時間戳轉(zhuǎn)為字符串

示例:

1)int()

# echo "123abc abc123 123abc123"|xargs -n1 | awk '{print int($0)}'
123
0
123
# awk 'BEGIN{print int(10/3)}'
3

2)sqrt()

獲取9的平方根:

# awk 'BEGIN{print sqrt(9)}'
3

3)rand()和srand()

rand()并不是每次運行就是一個隨機數(shù),會一直保持一個不變:
# awk 'BEGIN{print rand()}'
0.237788
當執(zhí)行srand()函數(shù)后,rand()才會發(fā)生變化,所以一般在awk著兩個函數(shù)結(jié)合生成隨機數(shù),但是也有很大幾率生成一樣:
# awk 'BEGIN{srand();print rand()}'
0.31687
如果想生成1-10的隨機數(shù)可以這樣:
# awk 'BEGIN{srand();print int(rand()*10)}'
4

如果想更完美生成隨機數(shù),還得做相應(yīng)的處理!

4)asort()和asorti()

# seq -f "str%.g" 5 |awk'{a[x++]=$0}END{s=asort(a,b);for(i=1;i<=s;i++)print b[i],i}'            
str1 1
str2 2
str3 3
str4 4
str5 5
# seq -f "str%.g" 5 |awk'{a[x++]=$0}END{s=asorti(a,b);for(i=1;i<=s;i++)print b[i],i}' 
0 1
1 2
2 3
3 4
4 5

asort將a數(shù)組的值放到數(shù)組b,a下標丟棄,并將數(shù)組b的總行號賦值給s,新數(shù)組b下標從1開始,然后遍歷。

5)sub()和gsub()

# tail /etc/services |awk'/blp5/{sub(/tcp/,"icmp");print $0}'
blp5           48129/icmp               #Bloomberg locator
blp5           48129/udp               #Bloomberg locator
# tail /etc/services |awk'/blp5/{gsub(/c/,"9");print $0}'
blp5           48129/t9p               #Bloomberg lo9ator
blp5           48129/udp               #Bloomberg lo9ator
# echo "1 2 2 3 4 5" |awk 'gsub(2,7,$2){print$0}'
1 7 2 3 4 5
# echo "1 2 3 a b c" |awk'gsub(/[0-9]/, '0'){print $0}'  
0 0 0 a b c

在指定行前后加一行:

# seq 5 | awk'NR==2{sub('/.*/',"txt\n&")}{print}'
1
txt
2
3
4
5
# seq 5 | awk'NR==2{sub('/.*/',"&\ntxt")}{print}'
1
2
txt
3
4
5

6)index()

# tail -n 5 /etc/services |awk '{printindex($2,"tcp")}'
7
0
7
0
7

7)length()

# tail -n 5 /etc/services |awk '{printlength($2)}'
9
9
9
9
9
統(tǒng)計數(shù)組的長度:
# tail -n 5 /etc/services |awk'{a[$1]=$2}END{print length(a)}'
3

8)split()

# echo -e "123#456#789\nabc#cde#fgh"|awk '{split($0,a);for(v in a)print a[v],v}'
123#456#789 1
abc#cde#fgh 1
# echo -e"123#456#789\nabc#cde#fgh" |awk '{split($0,a,"#");for(v ina)print a[v],v}'
123 1
456 2
789 3
abc 1
cde 2
fgh 3

9)substr()

# echo -e "123#456#789\nabc#cde#fgh"|awk '{print substr($0,4)}'                    
#456#789
#cde#fgh
# echo -e"123#456#789\nabc#cde#fgh" |awk '{print substr($0,4,5)}'
#456#
#cde#

10)tolower()和toupper()

# echo -e"123#456#789\nABC#cde#fgh" |awk '{print tolower($0)}'
123#456#789
abc#cde#fgh
# echo -e"123#456#789\nabc#cde#fgh" |awk '{print toupper($0)}'
123#456#789
ABC#CDE#FGH

11)時間處理

返回當前時間戳:
# awk 'BEGIN{print systime()}'
1483297766
將時間戳轉(zhuǎn)為日期和時間
# echo "1483297766" |awk '{printstrftime("%Y-%m-%d %H:%M:%S",$0)}'          
2017-01-01 14:09:26

8.3.8 I/O語句

語句

描述

getline

設(shè)置$0來自下一個輸入記錄

getline var

設(shè)置var來自下一個輸入記錄

command | getline  [var]

運行命令管道輸出到$0或var

next

停止當前處理的輸入記錄

print

打印當前記錄

printf fmt,  expr-list

格式化輸出

printf fmt,  expr-list >file

格式輸出和寫到文件

system(cmd-line)

執(zhí)行命令和返回狀態(tài)

print ...  >> file

追加輸出到文件

print ... |  command

打印輸出作為命令輸入

示例:

1)getline

獲取匹配的下一行:
# seq 5 |awk'/3/{getline;print}'
4
# seq 5 |awk'/3/{print;getline;print}'
3
4
在匹配的下一行加個星號:
# seq 5 |awk'/3/{getline;sub(".*","&*");print}'
4*
# seq 5 |awk'/3/{print;getline;sub(".*","&*")}{print}'
1
2
3
4*
5

2)getline var

把a文件的行追加到b文件的行尾:
# cat a
a
b
c
# cat b
1 one
2 two
3 three
# awk '{getlineline<"a";print $0,line}' b   
1 one a
2 two b
3 three c
把a文件的行替換b文件的指定字段:
# awk '{getlineline<"a";gsub($2,line,$2);print}' b  
1 a
2 b
3 c
把a文件的行替換b文件的對應(yīng)字段:
# awk '{getlineline<"a";gsub("two",line,$2);print}' b     
1 one
2 b
3 three
3)command| getline [var]
獲取執(zhí)行shell命令后結(jié)果的第一行:
# awk 'BEGIN{"seq 5"|getline var;print var}'
1
循環(huán)輸出執(zhí)行shell命令后的結(jié)果:
# awk 'BEGIN{while("seq 5"|getline)print}'
1
2
3
4
5

4)next

不打印匹配行:
# seq 5 |awk '{if($0==3){next}else{print}}'
1
2
4
5
刪除指定行:
# seq 5 |awk 'NR==1{next}{print $0}'
2
3
4
5
如果前面動作成功,就遇到next,后面的動作不再執(zhí)行,跳過。
或者:
# seq 5 |awk 'NR!=1{print}' 
2
3
4
5
把第一行內(nèi)容放到每行的前面:
# cat a
hello 
1 a
2 b
3 c
# awk 'NR==1{s=$0;next}{print s,$0}' a  
hello  1 a
hello  2 b
hello  3 c
# awk 'NR==1{s=$0}NF!=1{print s,$0}' a     
hello  1 a
hello  2 b
hello  3 c

5)system()

執(zhí)行shell命令判斷返回值:
# awk 'BEGIN{if(system("grep root /etc/passwd &>/dev/null")==0)print"yes";else print "no"}'
yes

6)打印結(jié)果寫到文件

# tail -n5 /etc/services |awk '{print $2 > "a.txt"}'
# cat a.txt
48049/tcp
48128/tcp
48128/udp
48129/tcp
48129/udp

7)管道連接shell命令

將結(jié)果通過grep命令過濾:
# tail -n5 /etc/services |awk '{print $2|"grep tcp"}'
48556/tcp
48619/tcp
49000/tcp

8.3.9 printf語句

格式化輸出,默認打印字符串不換行。

格式:printf [format] arguments

Format

描述

%s
一個字符串
%d,%i一個小數(shù)
%f一個浮點數(shù)

%.ns

輸出字符串,n是輸出幾個字符

%ni

輸出整數(shù),n是輸出幾個數(shù)字

%m.nf

輸出浮點數(shù),m是輸出整數(shù)位數(shù),n是輸出的小數(shù)位數(shù)

%x

不帶正負號的十六進制,使用a至f表示10到15

%X

不帶正負號的十六進制,使用A至F表示10至15

%%

輸出單個%

%-5s

左對齊,對參數(shù)每個字段左對齊,寬度為5

%-4.2f

左對齊,寬度為4,保留兩位小數(shù)

%5s

右對齊,不加橫線表示右對齊

示例: 

將換行符換成逗號:
# seq 5 |awk'{if($0!=5)printf "%s,",$0;else print $0}' 
1,2,3,4,5
小括號中的5是最后一個數(shù)字。
輸出一個字符:
# awk 'BEGIN{printf"%.1s\n","abc"}'       
a
保留一個小數(shù)點:
# awk 'BEGIN{printf "%.2f\n",10/3}'
3.33
格式化輸出:
# awk 'BEGIN{printf"user:%s\tpass:%d\n","abc",123}'
user:abc        pass:123
左對齊寬度10:
# awk 'BEGIN{printf "%-10s %-10s%-10s\n","ID","Name","Passwd"}'
ID         Name      Passwd
右對齊寬度10:
# awk 'BEGIN{printf "%10s %10s %10s\n","ID","Name","Passwd"}'  
        ID      Name     Passwd
打印表格:
# vi test.awk
BEGIN{
print"+--------------------+--------------------+";
printf"|%-20s|%-20s|\n","Name","Number";
print"+--------------------+--------------------+";
}
# awk -f test.awk
+--------------------+--------------------+
|Name                |Number              |
+--------------------+--------------------+
格式化輸出:
# awk -F: 'BEGIN{printf"UserName\t\tShell\n-----------------------------\n"}{printf"%-20s %-20s\n",$1,$7}END{print "END...\n"}' /etc/passwd
打印十六進制:
# awk 'BEGIN{printf "%x %X",123,123}'
7b 7B

8.3.10 自定義函數(shù)

格式:function name(parameter list) { statements }

示例:

# awk 'function myfunc(a,b){returna+b}BEGIN{print myfunc(1,2)}'     
3

博客地址:http://lizhenliang.blog.51cto.com

QQ群:323779636(Shell/Python運維開發(fā)群

8.3.11 需求案例

1)分析Nginx日志

日志格式:'$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"'

統(tǒng)計訪問IP次數(shù):
# awk '{a[$1]++}END{for(v in a)printv,a[v]}' access.log
統(tǒng)計訪問訪問大于100次的IP:
# awk '{a[$1]++}END{for(v ina){if(a[v]>100)print v,a[v]}}' access.log 
統(tǒng)計訪問IP次數(shù)并排序取前10:
# awk '{a[$1]++}END{for(v in a)print v,a[v]|"sort -k2 -nr |head -10"}' access.log
統(tǒng)計時間段訪問最多的IP:
# awk'$4>="[02/Jan/2017:00:02:00" &&$4<="[02/Jan/2017:00:03:00"{a[$1]++}END{for(v in a)print v,a[v]}'access.log
統(tǒng)計上一分鐘訪問量:
# date=$(date -d '-1 minute'+%d/%d/%Y:%H:%M)
# awk -vdate=$date '$4~date{c++}END{printc}' access.log
統(tǒng)計訪問最多的10個頁面:
# awk '{a[$7]++}END{for(vin a)print v,a[v]|"sort -k1 -nr|head -n10"}' access.log
統(tǒng)計每個URL數(shù)量和返回內(nèi)容總大?。?# awk '{a[$7]++;size[$7]+=$10}END{for(v ina)print a[v],v,size[v]}' access.log
統(tǒng)計每個IP訪問狀態(tài)碼數(shù)量:
# awk '{a[$1" "$9]++}END{for(v ina)print v,a[v]}' access.log
統(tǒng)計訪問IP是404狀態(tài)次數(shù):
# awk '{if($9~/404/)a[$1" "$9]++}END{for(i in a)printv,a[v]}' access.log

2)兩個文件對比

找出b文件在a文件相同記錄:

# seq 1 5 > a
# seq 3 7 > b
方法1:
# awk 'FNR==NR{a[$0];next}{if($0 in a)print$0}' a b 
3
4
5
# awk 'FNR==NR{a[$0];next}{if($0 in a)printFILENAME,$0}' a b
b 3
b 4
b 5
# awk 'FNR==NR{a[$0]}NR>FNR{if($0 ina)print $0}' a b  
3
4
5
# awk 'FNR==NR{a[$0]=1;next}(a[$0]==1)' a b  # a[$0]是通過b文件每行獲取值,如果是1說明有
# awk 'FNR==NR{a[$0]=1;next}{if(a[$0]==1)print}' a b
3
4
5
方法2:
# awk 'FILENAME=="a"{a[$0]}FILENAME=="b"{if($0 in a)print $0}' ab
3
4
5
方法3:
# awk 'ARGIND==1{a[$0]=1}ARGIND==2 &&a[$0]==1' a b    
3
4
5
找出b文件在a文件不同記錄:
方法1:
# awk 'FNR==NR{a[$0];next}!($0 in a)' ab             
6
7
# awk 'FNR==NR{a[$0]=1;next}(a[$0]!=1)' a b
# awk'FNR==NR{a[$0]=1;next}{if(a[$0]!=1)print}' a b
6
7
方法2:
# awk'FILENAME=="a"{a[$0]=1}FILENAME=="b" && a[$0]!=1' ab
方法3:
# awk 'ARGIND==1{a[$0]=1}ARGIND==2&& a[$0]!=1' a b

3)合并兩個文件

將a文件合并到b文件:

# cat a
zhangsan 20
lisi 23
wangwu 29
# cat b
zhangsan man
lisi woman
wangwu man
# awk 'FNR==NR{a[$1]=$0;next}{printa[$1],$2}' a b
zhangsan 20 man
lisi 23 woman
wangwu 29 man
# awk 'FNR==NR{a[$1]=$0}NR>FNR{printa[$1],$2}' a b         
zhangsan 20 man
lisi 23 woman
wangwu 29 man

將a文件相同IP的服務(wù)名合并:

# cat a
192.168.1.1:  httpd
192.168.1.1:  tomcat
192.168.1.2: httpd
192.168.1.2: postfix
192.168.1.3: mysqld
192.168.1.4: httpd
# awk 'BEGIN{FS=":";OFS=":"}{a[$1]=a[$1] $2}END{for(v in a)printv,a[v]}' a   
192.168.1.4: httpd
192.168.1.1: httpd tomcat
192.168.1.2: httpd postfix
192.168.1.3: mysqld

說明:數(shù)組a存儲是$1=a[$1] $2,第一個a[$1]是以第一個字段為下標,值是a[$1] $2,也就是$1=a[$1] $2,值的a[$1]是用第一個字段為下標獲取對應(yīng)的值,但第一次數(shù)組a還沒有元素,那么a[$1]是空值,此時數(shù)組存儲是192.168.1.1=httpd,再遇到192.168.1.1時,a[$1]通過第一字段下標獲得上次數(shù)組的httpd,把當前處理的行第二個字段放到上一次同下標的值后面,作為下標192.168.1.1的新值。此時數(shù)組存儲是192.168.1.1=httpd tomcat。每次遇到相同的下標(第一個字段)就會獲取上次這個下標對應(yīng)的值與當前字段并作為此下標的新值。

4)將第一列合并到一行

# cat file
1 2 3
4 5 6
7 8 9
# awk '{for(i=1;i<=NF;i++)a[i]=a[i]$i" "}END{for(vin a)print a[v]}' file    
1 4 7
2 5 8
3 6 9

說明:

for循環(huán)是遍歷每行的字段,NF等于3,循環(huán)3次。

讀取第一行時:

第一個字段:a[1]=a[1]1" "  值a[1]還未定義數(shù)組,下標也獲取不到對應(yīng)的值,所以為空,因此a[1]=1 。

第二個字段:a[2]=a[2]2" "  值a[2]數(shù)組a已經(jīng)定義,但沒有2這個下標,也獲取不到對應(yīng)的值,為空,因此a[2]=2 。

第三個字段:a[3]=a[3]3" "  值a[2]與上面一樣,為空,a[3]=3 。

讀取第二行時:

第一個字段:a[1]=a[1]4" "  值a[2]獲取數(shù)組a的2為下標對應(yīng)的值,上面已經(jīng)有這個下標了,對應(yīng)的值是1,因此a[1]=1 4

第二個字段:a[2]=a[2]5" "  同上,a[2]=2 5

第三個字段:a[3]=a[3]6" "  同上,a[2]=3 6

讀取第三行時處理方式同上,數(shù)組最后還是三個下標,分別是1=1 4 7,2=2 5 8,3=36 9。最后for循環(huán)輸出所有下標值。

5)字符串拆分,統(tǒng)計出現(xiàn)的次數(shù)

字符串拆分:

方法1:
# echo "hello world" |awk -F '''{print $1}'
h
# echo "hello" |awk -F '''{for(i=1;i<=NF;i++)print $i}'     
h
e
l
l
o
方法2:
# echo "hello" |awk'{split($0,a,"''");for(v in a)print a[v]}'
l
o
h
e
l

統(tǒng)計字符串中每個字母出現(xiàn)的次數(shù):

# echo "a.b.c,c.d.e" |awk -F'[.,]' '{for(i=1;i<=NF;i++)a[$i]++}END{for(v in a)print v,a[v]}'
a 1
b 1
c 2
d 1
e 1

5)費用統(tǒng)計

# cat a
zhangsan 8000 1
zhangsan 5000 1
lisi 1000 1
lisi 2000 1
wangwu 1500 1
zhaoliu 6000 1
zhaoliu 2000 1
zhaoliu 3000 1
# awk '{name[$1]++;cost[$1]+=$2;number[$1]+=$3}END{for(v in name)printv,cost[v],number[v]}' a
zhangsan 5000 1
lisi 3000 2
wangwu 1500 1
zhaoliu 11000 3

6)獲取數(shù)字字段最大值

# cat a
a b 1
c d 2
e f 3
g h 3
i j 2
獲取第三字段最大值:
# awk 'BEGIN{max=0}{if($3>max)max=$3}END{printmax}' a
3
打印第三字段最大行:
# awk 'BEGIN{max=0}{a[$0]=$3;if($3>max)max=$3}END{for(v in a)print v,a[v],max}' a
g h 3 3 3
e f 3 3 3
c d 2 2 3
a b 1 1 3
i j 2 2 3
# awk 'BEGIN{max=0}{a[$0]=$3;if($3>max)max=$3}END{for(v in a)if(a[v]==max)print v}'a
g h 3
e f 3

7)去除第一行和最后一行

# seq 5 |awk'NR>2{print s}{s=$0}'
2
3
4

讀取第一行,NR=1,不執(zhí)行print s,s=1

讀取第二行,NR=2,不執(zhí)行print s,s=2 (大于為真)

讀取第三行,NR=3,執(zhí)行print s,此時s是上一次p賦值內(nèi)容2,s=3

最后一行,執(zhí)行print s,打印倒數(shù)第二行,s=最后一行

獲取Nginx負載均衡配置端IP和端口:

# cat a
upstreamexample-servers1 {
   server 127.0.0.1:80 weight=1 max_fails=2fail_timeout=30s;
}
upstreamexample-servers2 {
   server 127.0.0.1:80 weight=1 max_fails=2fail_timeout=30s;
   server 127.0.0.1:82 backup;
}
# awk '/example-servers1/,/}/{if(NR>2){print s}{s=$2}}' a   
127.0.0.1:80
# awk '/example-servers1/,/}/{if(i>1)print s;s=$2;i++}' a  
# awk '/example-servers1/,/}/{if(i>1){print s}{s=$2;i++}}' a
127.0.0.1:80

讀取第一行,i初始值為0,0>1為假,不執(zhí)行print s,x=example-servers1,i=1

讀取第二行,i=1,1>1為假,不執(zhí)行prints,s=127.0.0.1:80,i=2

讀取第三行,i=2,2>1為真,執(zhí)行prints,此時s是上一次s賦值內(nèi)容127.0.0.1:80,i=3

最后一行,執(zhí)行print s,打印倒數(shù)第二行,s=最后一行。

這種方式與上面一樣,只是用i++作為計數(shù)器。


好了,AWK大部分知識點都在這了,能否提高你逼格呢?


如有問題歡迎加入Python運維開發(fā)群(249171211

第七章  Shell文本處理三劍客之a(chǎn)wk

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI