Linux三劍客詳解帶實驗

目錄

 grep

支持的正則 描述

輸出控制 描述

內容行控制 描述

示例:

sed

Usage:

命令 描述

地址 描述

匹配刪除(d)

 替換(s///)

 多重編輯(-e)

打印和刪除模式空間第一行(P 和 D)

標籤(:、b 和 t)

獲取總行數(#)

 選項

選項 描述

常用模式有:

示例:

內置變量

示例:

運算符 描述

示例:

 流程控制

語句 描述

示例:

printf 語句

 自定義函數

 需求案例

1)分析 Nginx 日誌

2)兩個文件對比

3)合併兩個文件

4)將第一列合併到一行

5)字符串拆分,統計出現的次數

6)統計平均成績

7)費用統計

8)數字字段最大值

9)去除第一行和最後一行


本篇博文整理於wiki

 grep

過濾來自一個文件或標準輸入匹配模式內容。

除了 grep 外,還有 egrep、fgrep。egrep 是 grep 的擴展,相當於 g

f,用的少。

Usage: grep [OPTION]... PATTERN [FILE]...

支持的正則 描述

-E,--extended-regexp 模式是擴展正則表達式(ERE)

-P,--perl-regexp 模式是 Perl 正則表達式

-e,--regexp=PATTERN 使用模式匹配,可指定多個模式匹

-f,--file=FILE 從文件每一行獲取匹配模式

-i,--ignore-case 忽略大小寫

-w,--word-regexp 模式匹配整個單詞

-x,--line-regexp 模式匹配整行

-v,--invert-match 打印不匹配的行

輸出控制 描述

-m,--max-count=NUM 輸出匹配的結果 num 數

-n,--line-number 打印行號

-H,--with-filename 打印每個匹配的文件名

-h,--no-filename 不輸出文件名

-o,--only-matching 只打印匹配的內容

-q,--quiet 不輸出正常信息

-s, --no-messages 不輸出錯誤信息

-r,--recursive 遞歸目錄

-c,--count 只打印每個文件匹配的行數

--include=FILE_PATTERN   只檢索匹配的文件

--exclude=FILE_PATTERN   跳過匹配的文件

--exclude-from=FILE       跳過匹配的文件,來自文件模式

--exclude-dir=PATTERN     跳過匹配的目錄

內容行控制 描述

-B,--before-context=NUM 打印匹配的前幾行

-A,--after-context=NUM 打印匹配的後幾行

-C,--context=NUM 打印匹配的前後幾行

--color[=WHEN], 匹配的字體顏色

示例:

1)輸出 b 文件中在 a 文件相同的行

# grep -f a b

2)輸出 b 文件中在 a 文件不同的行

# grep -v -f a b

3) 匹配多個模式

# echo "a bc de" |xargs -n1 |grep -e 'a' -e 'bc

a

bc

4)去除空格 http.conf 文件空行或開頭#號的行

# grep -E -v "^$|^#" /etc/httpd/conf/httpd.conf

5) 匹配開頭不分大小寫的單詞

# echo "A a b c" |xargs -n1 |grep -i a

# echo "A a b c" |xargs -n1 |grep '[Aa]'

A

a

6)只顯示匹配的字符串

# echo "this is a test" |grep -o 'is'

is

is

7)輸出匹配的前五個結果

# seq 1 20 |grep -m 5 -E '[0-9]{2}'

10

11

12

13

14

8)統計匹配多少行

# seq 1 20 |grep -c -E '[0-9]{2}'

11

9) 匹配 b 字符開頭的行

# echo "a bc de" |xargs -n1 |grep '^b'

bc

10) 匹配 de 字符結尾的行並輸出匹配的行

# echo "a ab abc abcd abcde" |xargs -n1 |grep -n 'de$'

5:abcde

11) 遞歸搜索/etc 目錄下包含 ip 的 conf 後綴文件

# grep -r '192.167.1.1' /etc --include *.conf

12) 排除搜索 bak 後綴的文件

# grep -r '192.167.1.1' /opt --exclude *.bak

13) 排除來自 file 中的文件

# grep -r '192.167.1.1' /opt --exclude-from file

14) 匹配 41 或 42 的數字

# seq 41 45 |grep -E '4[12]'

41

42

15) 匹配至少 2 個字符

# seq 13 |grep -E '[0-9]{2}'

10

11

12

13

16) 匹配至少 2 個字符的單詞,最多 3 個字符的單詞

# echo "a ab abc abcd abcde" |xargs -n1 |grep -E -w -o '[a-z]{2,3}'

ab

abc

17) 匹配所有 IP

# ifconfig |grep -E -o "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"

18) 打印匹配結果及後 3 行

# seq 1 10 |grep 5 -A 3

5

6

7

8

19) 打印匹配結果及前 3 行

# seq 1 10 |grep 5 -B 3

2

3

4

5

20) 打印匹配結果及前後 3 行

# seq 1 10 |grep 5 -C 3

2

3

4

5

6

7

8

21) 不顯示輸出

不顯示錯誤輸出:

# grep 'a' abc

grep: abc: No such file or directory

# grep -s 'a' abc

# echo $?

2

不顯示正常輸出:

# grep -q 'a' a.txt

grep 支持基礎和擴展正則表達式字符

sed

流編輯器,過濾和替換文本。

工作原理:sed 命令將當前處理的行讀入模式空間進行處理,處理完把結果輸出,並清空模式空間。然後再將下一行讀入模式空間進行處理輸出,以此類推,直到最後一行。還有一個空間叫保持空間,又稱暫存空間,可以暫時存放一些處理的數據,但不能直接輸出,只能放到模式空間輸出。

這兩個空間其實就是在內存中初始化的一個內存區域,存放正在處理的數據和臨時存放的數據。

Usage:

sed [OPTION]... {script-only-if-no-other-script} [input-file]...

sed [選項] '地址 命令' file

選項 描述

-n 不打印模式空間

-e 執行腳本、表達式來處理

-f 執行動作從文件讀取執行

-i 修改原文件

-r 使用擴展正則表達式

命令 描述

s/regexp/replacement/ 替換字符串

p 打印當前模式空間

P 打印模式空間的第一行

d 刪除模式空間,開始下一個循環

D 刪除模式空間的第一行,開始下一個循環

= 打印當前行號

a \text 當前行追加文本

i \text 當前行上面插入文本

c \text 所選行替換新文本

q 立即退出 sed 腳本

r 追加文本來自文件

: label label 爲 b 和 t 命令

b label 分支到腳本中帶有標籤的位置,如果分支不存在則分支到腳本

的末尾

t label 如果 s///是一個成功的替換,才跳轉到標籤

h H 複製/追加模式空間到保持空間

g G 複製/追加保持空間到模式空間

x 交換模式空間和保持空間內容

l 打印模式空間的行,並顯示控制字符$

n N 讀取/追加下一行輸入到模式空間

w filename 寫入當前模式空間到文件

! 取反、否定

& 引用已匹配字符串

地址 描述

first~step 步長,每 step 行,從第 first 開始

$ 匹配最後一行

/regexp/ 正則表達式匹配行

number 只匹配指定行

addr1,addr2 開始匹配 addr1 行開始,直接 addr2 行結束

addr1,+N 從 addr1 行開始,向後的 N 行

addr1,~N 從 addr1 行開始,到 N 行結束

藉助以下文本內容作爲示例講解:

# tail /etc/services

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

匹配打印(p) 

1)打印匹配 blp5 開頭的行

# tail /etc/services |sed -n '/^blp5/p'

blp5 48129/tcp # Bloomberg locator

blp5 48129/udp # Bloomberg locator

2)打印第行

# tail /etc/services |sed -n '1p'

nimgtw 48003/udp # Nimbus Gateway

3)打印第一行至第三行

# tail /etc/services |sed -n '1,3p'

nimgtw 48003/udp # Nimbus Gateway

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

isnetserv 48128/tcp # Image Systems Network Services

4)打印奇數行

# seq 10 |sed -n '1~2p'

1

3

5

7

9

5)打印匹配行及後一行

# tail /etc/services |sed -n '/blp5/,+1p'

blp5 48129/tcp # Bloomberg locator

blp5 48129/udp # Bloomberg locator

6)打印最後一行

# tail /etc/services |sed -n '$p'

iqobject 48619/udp # iqobject

7)不打印最後一行

# tail /etc/services |sed -n '$!p'

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

感嘆號也就是對後面的命令取反。

8)匹配範圍

# tail /etc/services |sed -n '/^blp5/,/^com/p'

blp5 48129/tcp # Bloomberg locator

blp5 48129/udp # Bloomberg locator

com-bardac-dw 48556/tcp # com-bardac-dw

匹配開頭行到最後一行:

# tail /etc/services |sed -n '/blp5/,$p'

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

以逗號分開兩個樣式選擇某個範圍。

9)引用系統變量,用引號

# a=1

# tail /etc/services |sed -n ''$a',3p'

# tail /etc/services |sed -n "$a,3p"

sed 命令用單引號時,裏面變量用單引號引起來,或者 sed 命令用雙引號,因爲雙引號解釋特殊符號原有意義。

匹配刪除(d)

刪除與打印使用方法類似,簡單舉幾個例子。

# tail /etc/services |sed '/blp5/d'

nimgtw 48003/udp # Nimbus Gateway

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

# tail /etc/services |sed '1d'

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

# tail /etc/services |sed '1~2d'

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

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 |sed '1,3d'

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

去除空格 http.conf 文件空行或開頭#號的行:

# sed '/^#/d;/^$/d' /etc/httpd/conf/httpd.conf

打印是把匹配的打印出來,刪除是把匹配的刪除,刪除只是不用-n 選項。

 替換(s///)

1)替換 blp5 字符串爲 test

# tail /etc/services |sed 's/blp5/test/'

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

isnetserv 48128/tcp # Image Systems Network Services

isnetserv 48128/udp # Image Systems Network Services

test 48129/tcp # Bloomberg locator

test 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

全局替換加 g:

# tail /etc/services |sed 's/blp5/test/g'

2)替換開頭是 blp5 的字符串並打印

# tail /etc/services |sed -n 's/^blp5/test/p'

test 48129/tcp # Bloomberg locator

test 48129/udp # Bloomberg locator

3)使用&命令引用匹配內容並替換

# tail /etc/services |sed 's/48049/&.0/'

3gpp-cbsp 48049.0/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

IP 加單引號:

# echo '10.10.10.1 10.10.10.2 10.10.10.3' |sed -r 's/[^ ]+/"&"/g'

"10.10.10.1" "10.10.10.2" "10.10.10.3"

4)對 1-4 行的 blp5 進行替換

# tail /etc/services | sed '1,4s/blp5/test/'

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

isnetserv 48128/tcp # Image Systems Network Services

isnetserv 48128/udp # Image Systems Network Services

test 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

5)對匹配行進行替換

# tail /etc/services | sed '/48129\/tcp/s/blp5/test/'

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

isnetserv 48128/tcp # Image Systems Network Services

isnetserv 48128/udp # Image Systems Network Services

test 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

6)二次匹配替換

# tail /etc/services |sed 's/blp5/test/;s/3g/4g/'

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

isnetserv 48128/tcp # Image Systems Network Services

isnetserv 48128/udp # Image Systems Network Services

test 48129/tcp # Bloomberg locator

test 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

7)分組使用,在每個字符串後面添加 123

# tail /etc/services |sed -r 's/(.*) (.*)(#.*)/\1\2test \3/'

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

isnetserv 48128/tcp test # Image Systems Network Services

isnetserv 48128/udp test # Image Systems Network Services

blp5 48129/tcp test # Bloomberg locator

blp5 48129/udp test # Bloomberg locator

blp5 48129/udp test # Bloomberg locator

com-bardac-dw 48556/tcp test # com-bardac-dw

com-bardac-dw 48556/udp test # com-bardac-dw

iqobject 48619/tcp test # iqobject

iqobject 48619/udp test # iqobject

matahari 49000/tcp test # Matahari Broker

第一列是第一個小括號匹配,第二列第二個小括號匹配,第三列一樣。將不變的字符串匹配分組,

再通過\數字按分組順序反向引用。

8)將協議與端口號位置調換

# tail /etc/services |sed -r 's/(.*)(\<[0-9]+\>)\/(tcp|udp)(.*)/\1\3\/\2\4/'

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

isnetserv tcp/48128 # Image Systems Network Services

isnetserv udp/48128 # Image Systems Network Services

blp5 tcp/48129 # Bloomberg locator

blp5 udp/48129 # Bloomberg locator

com-bardac-dw tcp/48556 # com-bardac-dw

com-bardac-dw udp/48556 # com-bardac-dw

iqobject tcp/48619 # iqobject

iqobject udp/48619 # iqobject

matahari tcp/49000 # Matahari Broker

9)位置調換

替換 x 字符爲大寫:

# echo "abc cde xyz" |sed -r 's/(.*)x/\1X/'

abc cde Xyz

456 與 cde 調換:

# echo "abc:cde;123:456" |sed -r 's/([^:]+)(;.*:)([^:]+$)/\3\2\1/'

abc:456;123:cde

10)註釋匹配行後的多少行

# seq 10 |sed '/5/,+3s/^/#/'

1

2

3

4

#5

#6

#7

#8

9

10

11)註釋指定多行

# seq 5 |sed -r 's/^3|^4/&#/'

1

2

3#

4#

5

# seq 5 |sed -r '/^3|^4/s/^/#/'

1

2

#3

#4

5

# seq 5 |sed -r 's/^3|^4/#\0/'

1

2

#3

#4

5

12)去除開頭和結尾空格或製表符

# echo " 1 2 3 " |sed 's/^[ \t]*//;s/[ \t]*$//'

1 2 3

 多重編輯(-e)

# tail /etc/services |sed -e '1,2d' -e 's/blp5/test/'

isnetserv 48128/udp # Image Systems Network Services

test 48129/tcp # Bloomberg locator

test 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

也可以使用分號分隔:

# tail /etc/services |sed '1,2d;s/blp5/test/'

7.2.5 添加新內容(a、i 和 c)

1)在 blp5 上一行添加 test

# tail /etc/services |sed '/blp5/i \test'

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

isnetserv 48128/tcp # Image Systems Network Services

isnetserv 48128/udp # Image Systems Network Services

test

blp5 48129/tcp # Bloomberg locator

test

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

2)在 blp5 下一行添加 test

# tail /etc/services |sed '/blp5/a \test'

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

test

blp5 48129/udp # Bloomberg locator

test

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)將 blp5 替換新行

# tail /etc/services |sed '/blp5/c \test'

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

isnetserv 48128/tcp # Image Systems Network Services

isnetserv 48128/udp # Image Systems Network Services

test

test

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

4)在指定行下一行添加一行

# tail /etc/services |sed '2a \test'

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

isnetserv 48128/tcp # Image Systems Network Services

test

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

5)在指定行前面和後面添加一行

# seq 5 |sed '3s/.*/txt\n&/'

1

2

txt

3

4

5

# seq 5 |sed '3s/.*/&\ntxt/'

1

2

3

txt

4

5

 讀取文件並追加到匹配行後(r)

# cat a.txt

123

456

# tail /etc/services |sed '/blp5/r a.txt'

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

123

456

blp5 48129/udp # Bloomberg locator

123

456

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

7.2.7 將匹配行寫到文件(w)

# tail /etc/services |sed '/blp5/w b.txt'

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

bdd48556/t# bdd

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

# cat b.txt

blp5 48129/tcp # Bloomberg locator

blp5 48129/udp # Bloomberg locator

 讀取下一行(n 和 N)

n 讀取下一行到模式空間。

N 追加下一行內容到模式空間,並以換行符\n 分隔。

1)打印匹配的下一行

# seq 5 |sed -n '/3/{n;p}'

4

2)打印偶數

# seq 6 |sed -n 'n;p'

2

4

6

sed 先讀取第一行 1,執行 n 命令,獲取下一行 2,此時模式空間是 2,執行 p 命令,打印模式空

間。 現在模式空間是 2,sed 再讀取 3,執行 n 命令,獲取下一行 4,此時模式空間爲 4,執行 p 命

令,以此類推。

3)打印奇數

# seq 6 |sed 'n;d'

1

3

5

sed 先讀取第一行 1,此時模式空間是 1,並打印模式空間 1,執行 n 命令,獲取下一行 2,執行 d

命令,刪除模式空間的 2,sed 再讀取 3,此時模式空間是 3,並打印模式空間,再執行 n 命令,獲

取下一行 4,執行 d 命令,刪除模式空間的 3,以此類推。

# seq 6 |sed -n 'p;n'

1

3

5

4)每三行執行一次 p 命令

# seq 6 |sed 'n;n;p'

1

2

3

3

4

5

6

6

sed 先讀取第一行 1,並打印模式空間 1,執行 n 命令,獲取下一行 2,並打印模式空間 2,再執行 n

命令,獲取下一行 3,執行 p 命令,打印模式空間 3。sed 讀取下一行 3,並打印模式空間 3,以此類

推。

5)每三行替換一次

方法 1:

# seq 6 |sed 'n;n;s/^/=/;s/$/=/'

1

2

=3=

4

5

=6=

我們只是把 p 命令改成了替換命令。

方法 2:

這次用到了地址匹配,來實現上面的效果:

# seq 6 |sed '3~3{s/^/=/;s/$/=/}'

1

2

=3=

4

5

=6=

當執行多個 sed 命令時,有時相互會產生影響,我們可以用大括號{}把他們括起來。

6)再看下 N 命令的功能

# seq 6 |sed 'N;q'

1

2

將兩行合併一行:

# seq 6 |sed 'N;s/\n//'

12

34

56

第一個命令:sed 讀取第一行 1,N 命令讀取下一行 2,並以\n2 追加,此時模式空間是 1\n2,再執

行 q 退出。

爲了進一步說明 N 的功能,看第二個命令:執行 N 命令後,此時模式空間是 1\n2,再執行把\n 替換

爲空,此時模式空間是 12,並打印。

# seq 5 |sed -n N;p

1

2

3

4

# seq 6 |sed -n 'N;p'

1

2

3

4

5

6

爲什麼第一個不打印 5 呢?

因爲 N 命令是讀取下一行追加到 sed 讀取的當前行,當 N 讀取下一行沒有內容時,則退出,也不會

執行 p 命令打印當前行。

當行數爲偶數時,N 始終就能讀到下一行,所以也會執行 p 命令。

7)打印奇數行數時的最後一行

# seq 5 |sed -n '$!N;p'

1

2

3

4

5

加一個滿足條件,當 sed 執行到最後一行時,用感嘆號不去執行 N 命令,隨後執行 p 命令。

打印和刪除模式空間第一行(P 和 D)

P 打印模式空間的第一行。

D 刪除模式空間的第一行。

1)打印奇數

# seq 6 |sed -n 'N;P'

1

3

5

2)保留最後一行

# seq 6 |sed 'N;D'

6

讀取第一行 1,執行 N 命令讀取下一行並追加到模式空間,此時模式空間是 1\n2,執行 D 命令刪除

模式空間第一行 1,剩餘 2。

讀取第二行,執行 N 命令,此時模式空間是 3\n4,執行 D 命令刪除模式空間第一行 3,剩餘 4。

以此類推,讀取最後一行打印時,而 N 獲取不到下一行則退出,不再執行 D,因此模式空間只剩餘 6

就打印。

 保持空間操作(h 與 H、g 與 G 和 x)

h 複製模式空間內容到保持空間(覆蓋)。

H 複製模式空間內容追加到保持空間。

g 複製保持空間內容到模式空間(覆蓋)。

G 複製保持空間內容追加到模式空間。

模式空間與保持空間內容換

1)將匹配的內容覆蓋到另一個匹配

# seq 6 |sed -e '/3/{h;d}' -e '/5/g'

1

2

4

3

6

h 命令把匹配的 3 複製到保持空間,d 命令刪除模式空間的 3。後面命令再對模式空間匹配 5,並用

g 命令把保持空間 3 覆蓋模式空間 5。

2)將匹配的內容放到最後

# seq 6 |sed -e '/3/{h;d}' -e '$G'

1

2

4

5

6

3

3)交換模式空間和保持空間

# seq 6 |sed -e '/3/{h;d}' -e '/5/x' -e '$G'

1

2

4

3

6

5

看後面命令,在模式空間匹配 5 並將保持空間的 3 與 5 交換,5 就變成了 3,。最後把保持空間的 5

追加到模式空間的。

4)倒敘輸出

# seq 5 |sed '1!G;h;$!d'

5

4

3

2

1

分析下:

1!G 第一行不執行把保持空間內容追加到模式空間,因爲現在保持空間還沒有數據。

h 將模式空間放到保持空間暫存。

$!d 最後一行不執行刪除模式空間的內容。

讀取第一行 1 時,跳過 G 命令,執行 h 命令將模式空間 1 複製到保持空間,執行 d 命令刪除模式空

間的 1。

讀取第二行 2 時,模式空間是 2,執行 G 命令,將保持空間 1 追加到模式空間,此時模式空間是

2\n1,執行 h 命令將 2\n1 覆蓋到保持空間,d 刪除模式空間。

讀取第三行 3 時,模式空間是 3,執行 G 命令,將保持空間 2\n1 追加到模式空間,此時模式空間是

3\n2\n1,執行 h 命令將模式空間內容複製到保持空間,d 刪除模式空間。

以此類推讀到第 行時模式空間是 執行 命令將保持空間的 追加模式空間

以此類推,讀到第 5 行時,模式空間是 5,執行 G 命令,將保持空間的 4\n3\n2\n1 追加模式空間,

然後複製到模式空間,5\n4\n3\n2\n1,不執行 d,模式空間保留,輸出。

由此可見,每次讀取的行先放到模式空間,再複製到保持空間,d 命令刪除模式空間內容,防止輸

出,再追加到模式空間,因爲追加到模式空間,會追加到新讀取的一行的後面,循環這樣操作, 就

把所有行一行行追加到新讀取行的後面,就形成了倒敘。

5)每行後面添加新空行

# seq 10 |sed G

1

2

3

4

5

6)打印匹配行的上一行內容

# seq 5 |sed -n '/3/{x;p};h'

2

讀取第一行 1,沒有匹配到 3,不執行{x;p},執行 h 命令將模式空間內容 1 覆蓋到保持空間。

讀取第二行 2,沒有匹配到 3,不執行{x;p},執行 h 命令將模式空間內容 2 覆蓋到保持空間。

讀取第三行 3,匹配到 3,執行 x 命令把模式空間 3 與保持空間 2 交換,再執行 p 打印模式空間 2.

以此類推。

7)打印匹配行到最後一行或下一行到最後一行

# seq 5 |sed -n '/3/,$p'

3

4

5

# seq 5 |sed -n '/3/,${h;x;p}'

3

4

5

# seq 5 |sed -n '/3/{:a;N;$!ba;p}'

3

4

5

# seq 5 |sed -n '/3/{n;:a;N;$!ba;p}'

4

5

匹配到 3 時,n 讀取下一行 4,此時模式空間是 4,執行 N 命令讀取下一行並追加到模式空間,此時

模式空間是 4\n5,標籤循環完成後打印模式空間 4\n5。

標籤(:、b 和 t)

標籤可以控制流,實現分支判斷。

: lable name 定義標籤

b lable 跳轉到指定標籤,如果沒有標籤則到腳本末尾

t lable 跳轉到指定標籤,前提是 s///命令執行成功

1)將換行符替換成逗號

方法 1:

# seq 6 |sed 'N;s/\n/,/'

1,2

3,4

5,6

這種方式並不能滿足我們的需求,每次 sed 讀取到模式空間再打印是新行,替換\n 也只能對 N 命令

追加後的 1\n2 這樣替換。

這時就可以用到標籤了:

# seq 6 |sed ':a;N;s/\n/,/;b a'

1,2,3,4,5,6

看看這裏的標籤使用,:a 是定義的標籤名,b a 是跳轉到 a 位置。

sed 讀取第

行 1,N 命令讀取下行 2,此時模式空間是 1\n2$,執行替換,此時模式空間是

1,2$,執行 b 命令再跳轉到標籤 a 位置繼續執行 N 命令,讀取下一行 3 追加到模式空間,此時模式

空間是 1,2\n3$,再替換,以此類推,不斷追加替換,直到最後一行 N 讀不到下一行內容退出。

方法 2:

# seq 6 |sed ':a;N;$!b a;s/\n/,/g'

1,2,3,4,5,6

先將每行讀入到模式空間,最後再執行全局替換。$!是如果是最後一行,則不執行 b a 跳轉,最後

執行全局替換。

# seq 6 |sed ':a;N;b a;s/\n/,/g'

1

2

3

4

5

6

可以看到,不加$!是沒有替換,因爲循環到 N 命令沒有讀到行就退出了,後面的替換也就沒執行。

2)每三個數字加個一個逗號

# echo "123456789" |sed -r 's/([0-9]+)([0-9]+{3})/\1,\2/'

123456,789

# echo "123456789" |sed -r ':a;s/([0-9]+)([0-9]+{3})/\1,\2/;t a'

123,456,789

# echo "123456789" |sed -r ':a;s/([0-9]+)([0-9]+{2})/\1,\2/;t a'

1,23,45,67,89

執行第一次時,替換最後一個,跳轉後,再對 123456 匹配替換,直到匹配替換不成功,不執行 t 命

令。

7.2.12 忽略大小寫匹配(I)

# echo -e "a\nA\nb\nc" |sed 's/a/1/Ig'

b

c

獲取總行數(#)

# seq 10 |sed -n '$='

 awk

awk 是一個處理文本的編程語言工具,能用簡短的程序處理標準輸入或文件、數據排序、計算以及

生成報表等等。

在 Linux 系統下默認 awk 是 gawk,它是 awk 的 GNU 版本。可以通過命令查看應用的版本:ls -l

/bin/awk

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

其中 pattern 表示 AWK 在數據中查找的內容,而 action 是在找到匹配內容時所執行的一系列命令。

花括號用於根據特定的模式對一系列指令進行分組。

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

在 awk 中,缺省的情況下將文本文件中的一行視爲一個記錄,逐行放到內存中處理,而將一行中的

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

$後跟數字,引用對應的字段,以逗號分隔,0 表示整個行。

 

 選項

選項 描述

-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{ } 給程序賦予初始狀態,先執行的工作

END{ } 程序結束之後執行的一些掃尾工作

/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

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

[]元字符的意思是符號其中任意個字符,也就是說每遇到個/或#時就分隔個字段,當用多個

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

3)變量賦值

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

123

系統變量作爲 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 模式是在處理文件之前執行該操作,常用於修改內置變量、變量賦值和打印輸出的頁眉或標

題。

例如:打印頁眉

# 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 模式是在程序處理完纔會執行。

例如:打印頁尾

# tail /etc/services |awk '{print $0}END{print "===\nEND......"}'

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

isnetserv 48128/tcp # Image Systems Network Service

isnetserv 48128/udp # Image Systems Network Service

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

匹配第一個字段是 8 個字符的行:

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

iqobject 48619/tcp # iqobject

iqobject 48619/udp # iqobject

matahari 49000/tcp # Matahari Broker

如果沒有匹配到,請查看你的 awk 版本(awk --version)是不是 3,因爲 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

對匹配範圍後記錄再次處理,例如匹配關鍵字下一行到最後一行:

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

4

5

另一種判斷真假的方式實現:

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

4

5

1 和 2 都不匹配 3,不執行後面{},執行 t,t 變量還沒賦值,爲空,空在 awk 中就爲假,就不打印

當前行。匹配到 3,執行 t=1,next 跳出,不執行 t。4 也不匹配 3,執行 t,t 的值上次賦值的 1,

爲真,打印當前行,以此類推。(非 0 的數字都爲真,所以 t 可以寫任意非 0 數字)

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

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

3

4

5

內置變量

變量名 描述

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

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

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

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

NF 統計當前記錄中字段個數

NR 統計記錄編號,每處理一行記錄,編號就會+1

FNR 統計記錄編號,每處理一行記錄,編號也會+1,與 NR 不同的是,處理

文件時,編號會重新計數。

ARGC 命令行參數數量

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

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

ENVIRON 當前系統的環境變量

FILENAME 輸出當前處理的文件名

IGNORECASE 忽略大小寫

SUBSEP 數組中下標的分隔符,默認爲"\034"

示例:

1)FS 和 OFS

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

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

root x

bin x

daemon x

adm x

lp x

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

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

認值

root x

bin x

daemon x

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

也可以通過字符串拼接實現分隔:

# 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

將輸出的換行符替換爲+號:

# seq 10 |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 是字段個數。

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

6

打印最後一個字段:

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

f

打印倒數第二個字段:

# 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 c d

排除第一個字段:

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

b c d e f

4)NR 和 FNR

NR 統計記錄編號,每處理一行記錄,編號就會+1,FNR 不同的是在統計第二個文件時會重新計數。

打印行數:

# 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

打印總行數:

# 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 的區別:

# 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 在處理第二個文件時,編號重新計數。同時也知道 awk 處理

兩個文件時,是合併到一起處理。

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

a1

b1

c1

c2

d2

e2

當 FNR==NR 時,說明在處理第一個文件內容,不等於時說明在處理第二個文件內容。

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

5)ARGC 和 ARGV

ARGC 是命令行參數數量

ARGV 是將命令行參數存到數組,元素由 ARGC 指定,數組下標從 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 調用系統變量。

# awk 'BEGIN{print ENVIRON["HOME"]}'

/root

如果是設置的環境變量,還需要用 export 導入到系統變量纔可以調用:

# awk 'BEGIN{print ENVIRON["a"]}'

# export a

# awk 'BEGIN{print ENVIRON["a"]}'

123

8)FILENAME

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

# 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 代表忽略大小寫。

 操作符 

運算符 描述

(....) 分組

$ 字段引用

++ -- 遞增和遞減

+ - ! 加號,減號,和邏輯否定

* / % 乘,除和取餘

+ - 加法,減法

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

< > <= >= != == 關係運算符

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

in 數組成員

&& || 邏輯 and,邏輯 or

?: 簡寫條件表達式:

expr1 ? expr2 : expr3

第一個表達式爲真,執行 expr2,否則執行 expr3

= += -= *= /= %= ^= 變量賦值運算符

須知:

在 awk 中,有 3 種情況表達式爲假:數字是 0,空字符串和未定義的值。

數值運算,未定義變量初始值爲 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)截取整數

# 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)感嘆號

打印奇數行:

# seq 6 |awk 'i=!i'

1

3

5

打印偶數行:

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

2

4

6

讀取第行:i 是未定義變量,也就是 i!0,!取反意思。感嘆號右邊是個布爾值,0 或空字符串爲假,非 0 或非空字符串爲真,!0 就是真,因此 i=1,條件爲真打印當前記錄。

沒有 print 爲什麼會打印呢?因爲模式後面沒有動作,默認會打印整條記錄。

讀取第二行:因爲上次 i 的值由 0 變成了 1,此時就是 i=!1,條件爲假不打印。

讀取第三行:上次條件又爲假,i 恢復初始值 0,取反,繼續打印。以此類推...

可以看出,運算時並沒有判斷行內容,而是利用布爾值真假判斷輸出當前行。

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

打印偶數行:

# seq 5 |awk '$0%2==0{print $0}'

2

4

打印奇數行:

# 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)判斷數組成員

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

yes

7)三目運算符

# awk 'BEGIN{print 1==1?"yes":"no"}' # 三目運算作爲一個表達式,裏面不允許寫 print

yes

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

no

yes

no

替換換行符爲逗號:

# 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

說明:讀取第一行時,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

 流程控制

1)if 語句

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

單分支:

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

3

也支持正則匹配判斷,一般在寫複雜語句時使用:

# echo "123abc#456cde 789aaa#aaabbb " |xargs -n1 |awk -F# '{if($2~/[0-9]/)print $2}'

456cde

# echo "123abc#456cde 789aaa#aaabbb " |xargs -n1 |awk -F# '{if($2!~/[0-9]/)print $2}'

aaabbb

# echo "123abc#456cde 789aaa#aaabbb" |xargs -n1 |awk -F# '$2!~/[0-9]/{print $2}'

aaabbb

雙分支:

# 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)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 語言風格

格式: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

都換行了,這並不是我們要的結果。怎麼改進呢?

# 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

在這種情況下,是不是就排除第一行和倒數第一行呢?我們正序打印看下

排除第一行:

# 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 語句遍歷數組

格式: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 跳過所有循環,continue 跳過當前循環。

# 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)刪除數組和元素

格式:

delete array[index] 刪除數組元素

delete array 刪除數組

# 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)exit 語句

格式:exit [ expression ]

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

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

# echo $?

123

 數組

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

awk 中數組稱爲關聯數組,不僅可以使用數字作爲下標,還可以使用字符串作爲下標。

數組元素的鍵和值存儲在 awk 程序內部的一個表中,該表採用散列算法,因此數組元素是隨機排

序。

數組格式:array[index]=value

1)自定義數組

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

test

2)通過 NR 設置記錄下標,下標從 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 循環遍歷數組

# 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 是數組的下標。

第一種 for 循環的結果是亂序的,剛說過,數組是無序存儲。

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

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

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

格式:exit [ expression ]

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

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

# echo $?

123

 數組

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

awk 中數組稱爲關聯數組,不僅可以使用數字作爲下標,還可以使用字符串作爲下標。

數組元素的鍵和值存儲在 awk 程序內部的一個表中,該表採用散列算法,因此數組元素是隨機排

序。

數組格式:array[index]=value

1)自定義數組

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

test

2)通過 NR 設置記錄下標,下標從 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 循環遍歷數組

# 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 是數組的下標。

第一種 for 循環的結果是亂序的,剛說過,數組是無序存儲。

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

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

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

# 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,沒循環次1

5)使用字段作爲下標

# 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)統計相同字段出現次數

# 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

第一個字段作爲下標,值被++初始化是 0,每次遇到下標(第一個字段)一樣時,對應的值就會被+1,因此實現了統計出現次數。

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

7)統計 TCP 連接狀態

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

9 LISTEN

6 ESTABLISHED

6 TIME_WAIT

8)只打印出現次數大於等於 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)去重

只打印重複的行:

# tail /etc/services |awk 'a[$1]++'

isnetserv 48128/udp # Image Systems Network Services

1)自定義數組

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

test

2)通過 NR 設置記錄下標,下標從 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 循環遍歷數組

# 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 是數組的下標。

第一種 for 循環的結果是亂序的,剛說過,數組是無序存儲。

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

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

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

# 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,沒循環次1

5)使用字段作爲下標

# 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)統計相同字段出現次數

# 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

第一個字段作爲下標,值被++初始化是 0,每次遇到下標(第一個字段)一樣時,對應的值就會被+1,因此實現了統計出現次數。

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

7)統計 TCP 連接狀態

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

9 LISTEN

6 ESTABLISHED

6 TIME_WAIT

8)只打印出現次數大於等於 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)去重

只打印重複的行:

# 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]+:0pt;">2 blp5

第一個字段作爲下標,值被++初始化是 0,每次遇到下標(第一個字段)一樣時,對應的值就會被+1,因此實現了統計出現次數。

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

7)統計 TCP 連接狀態

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

9 LISTEN

6 ESTABL

相關文章
相關標籤/搜索