目錄
本篇博文整理於wiki
過濾來自一個文件或標準輸入匹配模式內容。
除了 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 [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 命令用雙引號,因爲雙引號解釋特殊符號原有意義。
刪除與打印使用方法類似,簡單舉幾個例子。
# 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 選項。
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
# 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 刪除模式空間的第一行。
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。
標籤可以控制流,實現分支判斷。
: 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