1. 程式人生 > >運維之路-文字處理-三劍客(grep、sed、awk)

運維之路-文字處理-三劍客(grep、sed、awk)

轉載:https://blog.csdn.net/zisefeizhu/article/details/82526749

目錄

 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)通過++方式作為下標

# 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]++'

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

先明白一個情況,當值是 0 是為假,非 0 整數為真,知道這點就不難理解了。

只打印重複的行說明:當處理第一條記錄時,執行了++,初始值是 0 為假,就不列印,如果再遇到

相同的記錄,值就會+1,不為 0,則列印。

不列印重複的行說明:當處理第一條記錄時,執行了++,初始值是 0 為假,感嘆號取反為真,打

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

# 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)統計每個相同欄位的某欄位總數:

# 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)多維陣列

awk 的多維陣列,實際上 awk 並不支援多維陣列,而是邏輯上模擬二維陣列的訪問方式,比如

a[a,b]=1,使用 SUBSEP(預設\034)作為分隔下標欄位,儲存後是這樣 a\034b。

示例:

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

xy 123

我們可以重新複製 SUBSEP 變數,改變下標預設分隔符:

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

x:y 123

根據指定的欄位統計出現次數:

# cat file

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