Shell文本處理三劍客(二)

2018-06-08 17:08 更新

防偽碼:鋤禾日當(dāng)午,汗滴禾下土。


8.3 awk

awk 是一個(gè)處理文本的編程語言工具,能用簡(jiǎn)短的程序處理標(biāo)準(zhǔn)輸入或文件、數(shù)據(jù)排序、計(jì)算以及

生成報(bào)表等等。

在 Linux 系統(tǒng)下默認(rèn) awk 是 gawk,它是 awk 的 GNU 版本??梢酝ㄟ^命令查看應(yīng)用的版本:ls -l

/bin/awk

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

其中 pattern 表示 AWK 在數(shù)據(jù)中查找的內(nèi)容,而 action 是在找到匹配內(nèi)容時(shí)所執(zhí)行的一系列命令。

花括號(hào)用于根據(jù)特定的模式對(duì)一系列指令進(jìn)行分組。

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

在 awk 中,缺省的情況下將文本文件中的一行視為一個(gè)記錄,逐行放到內(nèi)存中處理,而將一行中的

某一部分作為記錄中的一個(gè)字段。用 1,2,3...數(shù)字的方式順序的表示行(記錄)中的不同字段。用

$后跟數(shù)字,引用對(duì)應(yīng)的字段,以逗號(hào)分隔,0 表示整個(gè)行。

8.3.1  選項(xiàng)

選項(xiàng)  描述

-f program-file  從文件中讀取 awk 程序源文件

-F fs  指定 fs 為輸入字段分隔符

-v var=value  變量賦值

--posix  兼容 POSIX 正則表達(dá)式

--dump-variables=[file]  把 awk 命令時(shí)的全局變量寫入文件,

默認(rèn)文件是 awkvars.out

--profile=[file]  格式化 awk 語句到文件,默認(rèn)是 awkprof.out

8.3.2  模式

常用模式有:

Pattern  Description

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

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

/regular expression/  為每個(gè)輸入記錄匹配正則表達(dá)式

pattern && pattern  邏輯 and,滿足兩個(gè)模式

pattern || pattern  邏輯 or,滿足其中一個(gè)模式

! pattern  邏輯 not,不滿足模式

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

而動(dòng)作呢,就是下面所講的 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)指定分隔符,打印指定字段

打印第二字段,默認(rèn)以空格分隔:

# tail -n3 /etc/services |awk '{print $2}'

48049/tcp

48128/tcp

48128/udp

指定冒號(hào)為分隔符打印第一字段:

# awk -F ':' '{print $1}' /etc/passwd

root

bin

daemon

adm

lp

sync

......

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

# tail -n3 /etc/services |awk -F'[/#]' '{print $3}'

iqobject

iqobject

Matahari Broker

# 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

Matahari Broker

# tail -n3 /etc/services |awk -F'[ /]+' '{print $2}'

48619

48619

49000

[]元字符的意思是符號(hào)其中任意一個(gè)字符,也就是說每遇到一個(gè)/或#時(shí)就分隔一個(gè)字段,當(dāng)用多個(gè)

分隔符時(shí),就能更方面處理字段了。

3)變量賦值

# awk -v a=123 'BEGIN{print a}'

123

系統(tǒng)變量作為 awk 變量的值:

# a=123

# awk -v a=$a 'BEGIN{print a}'

123

或使用單引號(hào)

# 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)置變量、變量賦值和打印輸出的頁(yè)眉或標(biāo)

題。

例如:打印頁(yè)眉

# 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 模式是在程序處理完才會(huì)執(zhí)行。

例如:打印頁(yè)尾

# 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 Service Protocol

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

匹配第一個(gè)字段是 8 個(gè)字符的行:

# tail /etc/services |awk '/^[a-z0-9]{8} /{print $0}'

iqobject 48619/tcp # iqobject

iqobject 48619/udp # iqobject

matahari 49000/tcp # Matahari Broker

如果沒有匹配到,請(qǐng)查看你的 awk 版本(awk --version)是不是 3,因?yàn)?4 才支持{}

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

對(duì)匹配范圍后記錄再次處理,例如匹配關(guān)鍵字下一行到最后一行:

# seq 5 |awk '/3/,/^$/{printf /3/?"":$0"\n"}'

4

5

另一種判斷真假的方式實(shí)現(xiàn):

# seq 5 |awk '/3/{t=1;next}t'

4

5

1 和 2 都不匹配 3,不執(zhí)行后面{},執(zhí)行 t,t 變量還沒賦值,為空,空在 awk 中就為假,就不打印

當(dāng)前行。匹配到 3,執(zhí)行 t=1,next 跳出,不執(zhí)行 t。4 也不匹配 3,執(zhí)行 t,t 的值上次賦值的 1,

為真,打印當(dāng)前行,以此類推。(非 0 的數(shù)字都為真,所以 t 可以寫任意非 0 數(shù)字)

如果想打印匹配行都最后一行,就可以這樣了:

# seq 5 |awk '/3/{t=1}t'

3

4

5


8.3.3  內(nèi)置變量

變量名  描述

FS  輸入字段分隔符,默認(rèn)是空格或制表符

OFS  輸出字段分隔符,默認(rèn)是空格

RS  輸入記錄分隔符,默認(rèn)是換行符\n

ORS  輸出記錄分隔符,默認(rèn)是換行符\n

NF  統(tǒng)計(jì)當(dāng)前記錄中字段個(gè)數(shù)

NR  統(tǒng)計(jì)記錄編號(hào),每處理一行記錄,編號(hào)就會(huì)+1

FNR  統(tǒng)計(jì)記錄編號(hào),每處理一行記錄,編號(hào)也會(huì)+1,與 NR 不同的是,處理第二個(gè)

文件時(shí),編號(hào)會(huì)重新計(jì)數(shù)。

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

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

ARGIND  當(dāng)前正在處理的文件索引值。第一個(gè)文件是 1,第二個(gè)文件是 2,以此類推

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

FILENAME  輸出當(dāng)前處理的文件名

IGNORECASE  忽略大小寫

SUBSEP  數(shù)組中下標(biāo)的分隔符,默認(rèn)為"\034"

示例:

1)FS 和 OFS

在程序開始前重新賦值 FS 變量,改變默認(rèn)分隔符為冒號(hào),與-F 一樣。

# awk 'BEGIN{FS=":"}{print $1,$2}' /etc/passwd |head -n5

root x

bin x

daemon x

adm x

lp x

也可以使用-v 來重新賦值這個(gè)變量:

# awk -vFS=':' '{print $1,$2}' /etc/passwd |head -n5 # 中間逗號(hào)被換成了 OFS 的默

認(rèn)值

root x

bin x

daemon x

adm x

lp x

由于 OFS 默認(rèn)以空格分隔,反向引用多個(gè)字段分隔的也是空格,如果想指定輸出分隔符這樣:

# awk 'BEGIN{FS=":";OFS=":"}{print $1,$2}' /etc/passwd |head -n5

root:x

bin:x

daemon:x

adm:x

lp:x

也可以通過字符串拼接實(shí)現(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 默認(rèn)是\n 分隔每行,如果想指定以某個(gè)字符作為分隔符來處理記錄:

# echo "www.baidu.com/user/test.html" |awk 'BEGIN{RS="/"}{print $0}'

www.baidu.com

user

test.html

RS 也支持正則,簡(jiǎn)單演示下:

# seq -f "str%02g" 10 |sed 'n;n;a\-----' |awk 'BEGIN{RS="-+"}{print $1}'

str01

str04

str07

str10

將輸出的換行符替換為+號(hào):

# seq 10 |awk 'BEGIN{ORS="+"}{print $0}'

1+2+3+4+5+6+7+8+9+10+

替換某個(gè)字符:

# tail -n2 /etc/services |awk 'BEGIN{RS="/";ORS="#"}{print $0}'

iqobject 48619#udp # iqobject

matahari 49000#tcp # Matahari Broker

3)NF

NF 是字段個(gè)數(shù)。

# echo "a b c d e f" |awk '{print NF}'

6

打印最后一個(gè)字段:

# echo "a b c d e f" |awk '{print $NF}'

f

打印倒數(shù)第二個(gè)字段:

# echo "a b c d e f" |awk '{print $(NF-1)}'

e

排除最后兩個(gè)字段:

# echo "a b c d e f" |awk '{$NF="";$(NF-1)="";print $0}'

a b c d

排除第一個(gè)字段:

# echo "a b c d e f" |awk '{$1="";print $0}'

b c d e f

4)NR 和 FNR

NR 統(tǒng)計(jì)記錄編號(hào),每處理一行記錄,編號(hào)就會(huì)+1,F(xiàn)NR 不同的是在統(tǒng)計(jì)第二個(gè)文件時(shí)會(huì)重新計(jì)數(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

打印第三行第二個(gè)字段:

# 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 每處理一行就會(huì)+1,而 FNR 在處理第二個(gè)文件時(shí),編號(hào)重新計(jì)數(shù)。同時(shí)也知道 awk 處理

兩個(gè)文件時(shí),是合并到一起處理。

# awk 'FNR==NR{print $0"1"}FNR!=NR{print $0"2"}' a b

a1

b1

c1

c2

d2

e2

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

一般 FNR 在處理多個(gè)文件時(shí)會(huì)用到,下面會(huì)講解。

5)ARGC 和 ARGV

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

ARGV 是將命令行參數(shù)存到數(shù)組,元素由 ARGC 指定,數(shù)組下標(biāo)從 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 是當(dāng)前正在處理的文件索引值,第一個(gè)文件是 1,第二個(gè)文件是 2,以此類推,從而可以通

過這種方式判斷正在處理哪個(gè)文件。

# 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 是當(dāng)前處理文件的文件名。

# awk 'FNR==NR{print FILENAME"->"$0}FNR!=NR{print FILENAME"->"$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 代表忽略大小寫。

8.3.4 4 操作符

運(yùn)算符  描述

(....)  分組

$  字段引用

++ --  遞增和遞減

+ - !  加號(hào),減號(hào),和邏輯否定

* / %  乘,除和取余

+ -  加法,減法

| |&  管道,用于 getline,print 和 printf

< > <= >= != ==  關(guān)系運(yùn)算符

~ !~  正則表達(dá)式匹配,否定正則表達(dá)式匹配

in  數(shù)組成員

&& ||  邏輯 and,邏輯 or

?:  簡(jiǎn)寫條件表達(dá)式:

expr1 ? expr2 : expr3

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

= += -= *= /= %= ^=  變量賦值運(yùn)算符

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

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

舉例測(cè)試:

# 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)感嘆號(hào)

打印奇數(shù)行:

# seq 6 |awk 'i=!i'

1

3

5

讀取第一行,i 是未定義變量,也就是 i=!0,!取反意思。感嘆號(hào)右邊是個(gè)布爾值,0 或空字符串為

假,非 0 或非空字符串為真,!0 就是真,因此 i=1,條件為真打印當(dāng)前記錄。

沒有 print 為什么會(huì)打印呢?因?yàn)槟J胶竺鏇]有動(dòng)作,默認(rèn)會(huì)打印整條記錄。

讀取第二行,因?yàn)樯洗?i 的值由 0 變成了 1,此時(shí)就是 i=!1,條件為假不打印。

讀取第三行,上次條件又為假,i 恢復(fù)初始值 0,取反,繼續(xù)打印。以此類推...

可以看出,運(yùn)算時(shí)并沒有判斷行內(nèi)容,而是利用布爾值真假判斷輸出當(dāng)前行。

打印偶數(shù)行:

# seq 6 |awk '!(i=!i)'

2

4

6

2)不匹配某行

# tail /etc/services |awk '!/blp5/{print $0}'

3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service

isnetserv 48128/tcp # Image Systems Network Services

isnetserv 48128/udp # Image Systems 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 # Matahari Broker

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)正則表達(dá)式匹配

# 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)三目運(yùn)算符

# awk 'BEGIN{print 1==1?"yes":"no"}' # 三目運(yùn)算作為一個(gè)表達(dá)式,里面不允許寫 print

yes

# seq 3 |awk '{print $0==2?"yes":"no"}'

no

yes

no

替換換行符為逗號(hào):

# seq 5 |awk '{print n=(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

說明:讀取第一行時(shí),n 沒有變量,為假輸出$0 也就是 1,并賦值變量 n,讀取第二行時(shí),n 是 1 為

真,輸出 1,2 以此類推,后面會(huì)一直為真。

每三行后面添加新一行:

# 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 5 流程控制

1 1 )f if  語句

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

單分支:

# seq 5 |awk '{if($0==3)print $0}'

3

雙分支:

# seq 5 |awk '{if($0==3)print $0;else print "no"}'

no

no

3

no

no

多分支:

# cat file

1 2 3

4 5 6

7 8 9

# awk '{if($1==4){print "1"} else if($2==5){print "2"} else if($3==6){print "3"} else

{print "no"}}' file

no

1

no

2 2 )e while  語句

格式:while (condition) statement

遍歷打印所有字段:

# awk '{i=1;while(i<=NF){print $i;i++}}' file

1

2

3

4

5

6

7

8

9

awk 是按行處理的,每次讀取一行,并遍歷打印每個(gè)字段。

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

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

遍歷打印所有字段:

# cat file

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é)果。怎么改進(jìn)呢?

# awk '{for(i=NF;i>=1;i--){printf $i" "};print ""}' file # print 本身就會(huì)新打印一行

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 加單引號(hào):

# 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 4 )r 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 5 )k break 和 和 e continue  語句

break 跳過所有循環(huán),continue 跳過當(dāng)前循環(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 6 )刪除數(shù)組和元素

格式:

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

delete array 刪除數(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)print v,a[v]}'

4 str4

5 str5

1 str1

2 str2

7 7 )t exit  語句

格式:exit [ expression ]

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

# seq 5 |awk '{if($0~/3/)exit (123)}'

# echo $?

123

8.3.6 6 數(shù)組

數(shù)組:存儲(chǔ)一系列相同類型的元素,鍵/值方式存儲(chǔ),通過下標(biāo)(鍵)來訪問值。

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

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

序。

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

1)自定義數(shù)組

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

test

2)通過 NR 設(shè)置記錄下標(biāo),下標(biāo)從 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}'

zabbix 4

user 5

admin 1

systemd-bus-proxy 2

systemd-network 3

# tail -n5 /etc/passwd |awk -F: '{a[NR]=$1}END{for(i=1;i<=NR;i++)print a[i],i}'

admin 1

systemd-bus-proxy 2

systemd-network 3

zabbix 4

user 5

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

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

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

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

4)通過++方式作為下標(biāo)

# tail -n5 /etc/passwd |awk -F: '{a[x++]=$1}END{for(i=0;i<=x-1;i++)print a[i],i}'

admin 0

systemd-bus-proxy 1

systemd-network 2

zabbix 3

user 4

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

5)使用字段作為下標(biāo)

# tail -n5 /etc/passwd |awk -F: '{a[$1]=$7}END{for(v in a)print a[v],v}'

/sbin/nologin admin

/bin/bash user

/sbin/nologin systemd-network

/sbin/nologin systemd-bus-proxy

/sbin/nologin zabbix

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

# tail /etc/services |awk '{a[$1]++}END{for(v in a)print a[v],v}'

2 com-bardac-dw

1 3gpp-cbsp

2 iqobject

1 matahari

2 isnetserv

2 blp5

# tail /etc/services |awk '{a[$1]+=1}END{for(v in a)print a[v],v}'

2 com-bardac-dw

1 3gpp-cbsp

2 iqobject

1 matahari

2 isnetserv

2 blp5

# tail /etc/services |awk '/blp5/{a[$1]++}END{for(v in a)print a[v],v}'

2 blp5

第一個(gè)字段作為下標(biāo),值被++初始化是 0,每次遇到下標(biāo)(第一個(gè)字段)一樣時(shí),對(duì)應(yīng)的值就會(huì)被

+1,因此實(shí)現(xiàn)了統(tǒng)計(jì)出現(xiàn)次數(shù)。

想要實(shí)現(xiàn)去重的的話就簡(jiǎn)單了,只要打印下標(biāo)即可。

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

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

9 LISTEN

6 ESTABLISHED

6 TIME_WAIT

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

# tail /etc/services |awk '{a[$1]++}END{for(v in a) if(a[v]>=2){print a[v],v}}'

2 com-bardac-dw

2 iqobject

2 isnetserv

2 blp5

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

不打印重復(fù)的行:

# 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

先明白一個(gè)情況,當(dāng)值是 0 是為假,非 0 整數(shù)為真,知道這點(diǎn)就不難理解了。

只打印重復(fù)的行說明:當(dāng)處理第一條記錄時(shí),執(zhí)行了++,初始值是 0 為假,就不打印,如果再遇到

相同的記錄,值就會(huì)+1,不為 0,則打印。

不打印重復(fù)的行說明:當(dāng)處理第一條記錄時(shí),執(zhí)行了++,初始值是 0 為假,感嘆號(hào)取反為真,打

印,如果再遇到相同的記錄,值就會(huì)+1,不為 0 為真,取反為假就不打印。

# tail /etc/services |awk '{if(a[$1]++)print $1}'

isnetserv

blp5

com-bardac-dw

iqobject

使用三目運(yùn)算:

# 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)計(jì)每個(gè)相同字段的某字段總數(shù):

# tail /etc/services |awk -F'[ /]+' '{a[$1]+=$2}END{for(v in a)print v, a[v]}'

com-bardac-dw 97112

3gpp-cbsp 48049

iqobject 97238

matahari 49000

isnetserv 96256

blp5 96258

11)多維數(shù)組

awk 的多維數(shù)組,實(shí)際上 awk 并不支持多維數(shù)組,而是邏輯上模擬二維數(shù)組的訪問方式,比如

a[a,b]=1,使用 SUBSEP(默認(rèn)\034)作為分隔下標(biāo)字段,存儲(chǔ)后是這樣 a\034b。

示例:

# awk 'BEGIN{a["x","y"]=123;for(v in a) print v,a[v]}'

xy 123

我們可以重新復(fù)制 SUBSEP 變量,改變下標(biāo)默認(rèn)分隔符:

# awk 'BEGIN{SUBSEP=":";a["x","y"]=123;for(v in a) print v,a[v]}'

x:y 123

根據(jù)指定的字段統(tǒng)計(jì)出現(xiàn)次數(shù):

# cat a

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


本文出自 “一盞燭光” 博客,謝絕轉(zhuǎn)載!

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)