正則運算式及文本處理三劍客
- 前言
- 一、正則運算式
- 基本正則運算式元字符:
- 擴展正則運算式元字符(需要egrep或者轉義)
- 二、文本處理三劍客
- grep
- sed
- awk
前言
玩轉Linux作業系統必須會shell,會shell必須知道正則運算式及grep、sed、awk文本處理三劍客,好多年前有個大佬告訴我,不知道正則不要說自己會Linux,為此我專門學習這部分內容,這篇文章是對我學習內容的整理,也是我經常查閱的一篇筆記,也希望他可以幫助到大家,共同進步,瑞思拜~
一、正則運算式
基本正則運算式元字符:
元字符 功能 示例
^ 行首定位符 ^love grep '^root' /etc/passwd
$ 行尾定位符 love$ grep 'root$' /etc/passwd
. 匹配單個字符 l..e grep '^r..t' /etc/passwd
- 匹配0個或多個前導字符 ab*love grep 'ro*t' /etc/passwd
.* 任意多個字符 grep 'r.*ot' /etc/passwd # r開頭ot結尾
[] 匹配指定范圍內的一個字符 [lL]ove grep '[rx]oot' /etc/passwd
[ - ] 匹配指定范圍內的一個字符 [a-z0-9]ove grep '[a-z0-9]oot' /etc/passwd
[^] 匹配不在指定組內的字符 [^a-z0-9]ove grep '[^a-z0-9]oot' /etc/passwd
^[^] 括號外尖號是指匹配,匹配括號內的任意一個字符 括號內尖號是指取反,匹配不在括號內的任意字符
\ 用來轉義元字符 love\. 匹配love.
\< 詞首定位符 \<love 定位單詞首
\> 詞尾定位符 love\> 定位單詞尾
\(..\) 匹配稍后使用的字符的標簽 :% s/172.16.130.1/172.16.130.5/
:% s/\(172.16.130.\)1/\15/
:% s/\(172.\)\(16.\)\(130.\)1/\1\2\35/
:3,9 s/\(.*\)/#\1/
x\{m\} 字符x重復出現m次 lo\{5\}ve
x\{m,\} 字符x重復出現m次以上 lo\{5,\}ve
x\{m,n\} 字符x重復出現m到n次 lo\{5,10\}ve
擴展正則運算式元字符(需要egrep或者轉義)
元字符 功能 示例
+ 匹配一個或多個前導字符 [a-z]+ove
? 匹配零個或一個前導字符 lo?ve
a|b 匹配a或b love|hate
() 組字符 loveable|rs love(able|rs) ov+ (ov)+
(..)(..)\1\2 標簽匹配字符 (love)able\1er
x{m} 字符x重復m次 lo{5}ve
x{m,} 字符x重復至少m次 lo{5,}ve
x{m,n} 字符x重復m到n次 lo{5,10}ve
二、文本處理三劍客
grep
grep使用的元字符:
grep: 使用基本元字符集 ^, $, ., *, [], [^], \< \>,\(\),\{\}, \+, \|
egrep(grep -E): 使用擴展元字符集 ?, +, { }, |, ( )
grep也可以使用擴展集中的元字符,僅需要對這些元字符前置一個反斜線(轉義)
\w 所有字母與數字,稱為字符[a-zA-Z0-9] 'l[a-zA-Z0-9]*ve' 'l\w*ve'
\W 所有字母與數字之外的字符,稱為非字符 'love[^a-zA-Z0-9]+' 'love\W+'
\b 詞邊界 '\<love\>' '\blove\b'
grep不支持\d、\D、\s、\S,\d匹配數字,\D匹配非數字,\s匹配空白,\S匹配非空白
grep常用選項:
-i --ignore-case # 忽略大小寫
-v --invert-match # 反向查找,只顯示不匹配的行
-n --line-number # 輸出的同時列印行號
-e --regexp=PATTERN
grep -e "a" -e "b" file # 同時過濾多個條件
-f --file=FILE
grep -f a b # 輸出b檔案中與a相同的行
-c --count
grep -c "1" a # 輸出a檔案中匹配到1的行數
-o --only-matching
ifconfig |grep -E -o "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" # 列印所有ip
sed
sed使用的元字符
使用基本元字符集 ^, $, ., *, [], [^], \< \>,\(\),\{\}
使用擴展元字符集 ?, +, { }, |, ( )
sed選項
sed -r 列印結果,沒有真正執行
sed -i 真正執行
sed -n 靜默輸出(只列印處理過的行)
sed常用用法
sed -n '10,20p' 只顯示匹配到的行
sed -r 's/root/alice/gi' /etc/passwd 忽略大小寫且全域替換root>alice
sed -r 's#root#alice#gi' /etc/passwd 替換 不需要定義分隔符 直接使用
sed -r '\#root#d' /etc/passwd 查找 要定義分隔符
sed -r '/^[ \t]*#/d' passwd 洗掉`#`開頭的行
sed -r '/^[ \t]*$/d' passwd 洗掉空行
sed -r '/^[ \t]*#/d; /^[ \t]*$/d' passwd 洗掉注釋行和空行 ;號隔開
sed -r '3,$ s/^#*/#/' passwd 將行首零個或多個#換成一個#
sed -r '30,50 s/^[ \t]*#*/#/' passwd 將行首帶有空格的零個#或多個#換成一個# 全部注釋
sed ':label;N;s/\n/ /;b label' 回車符轉行為空格
sed定址
sed -r 'd' passwd 全部洗掉
sed -r '3d' /etc/passwd 洗掉第3行
sed -r '1,3d' /etc/passwd 洗掉1-3行
sed -r '/root/d' /etc/passwd 洗掉帶root的行
sed -r 's/root/alice/g' /etc/passwd 全域替換root為alice
sed -r '/^adm/,20d' /etc/passwd 從adm開頭的行洗掉到第20行
sed -r '/^adm/,+20d' /etc/passwd 洗掉adm開頭的行再洗掉其行后20行
sed常用引數
d 洗掉行
s 用一個字串替換另一個
s 替換標志
g 全域替換
i 忽略大小寫
sed -r 's/(.*)/#\1/' passwd
sed -r 's/(.*)/#&/' passwd &代表在查找串中匹配到的內容
sed -r 's/^/#/' passwd 全部注釋
sed -r 's/(.)(.)(.*)/\1oooo\2\3/' passwd 在第二個字母前添加內容 oooo
sed -r 's/(192)(.)/\1\2xx\2/' a.file
192.xx.168.1.1 10.19.200.200
sed -n "s/192/xx/p" a 替換并列印
xx.168.1.1 10.19.200.200
r 讀取檔案
sed -r '2r 1.txt' a.txt 把a.txt的前2行放到1.txt最前面
w 寫入檔案
sed -r '3,$w 1.txt' a.txt 把a.txt的3行到最后一行保存到1.txt
aic 插入
sed -r '2a 1111111111111' 1.txt 在第二行后面追加111111111111
sed -r '2i 1111111111111' 1.txt 在第二行前面插入一行1111111111111
sed -r '2c 1111111111111' 1.txt 第二行替換成1111111111
sed -r '/^192/a abc123' a 在以192開頭的行后面追加abc123
192.168.1.1 10.19.200.200
abc123
192.168.1.1 10.19.200.200
abc123
sed空間操作
n 讀取下一行到模式空間,如果沒有下一行則執行 q 退出
N 追加下一行內容到模式空間,并以換行符\n分割
q 退出
p 列印模式空間所有內容
P 列印模式空間第一行
seq 6 |sed -n 'n;p' 列印偶數行
seq 6 |sed 'N;q' 列印1\n2
d 洗掉模式空間所有內容
D 洗掉模式空間第一行,以`\n`分割,放棄之后的命令,但是對剩余模式空間重新執行sed
seq 6 |sed -n 'N;P' 列印奇數行
seq 6 |sed 'N;D' 列印最后一行
讀取1,執行N,模式空間為1\n2,執行D,洗掉1剩余2,執行N,模式空間為2\n3,執行D,洗掉2剩余3,依此類推,得出5,執行N,條件失敗退出,
h/H 模式空間**覆寫/追加**到暫存空間
g/G 暫存空間**覆寫/追加**到模式空間
sed -r '1h;$G' passwd 把第1行復制到最后一行
sed -r '1{h;d};$G' passwd 把第1行剪切到最后一行
sed -r '1h; 2,$g' passwd 把其它行全部替換成第1行
sed -r '1h; 2,3H; $G' passwd 把前3行復制到最后三行
sed -r '1!G;h;$!d' 1 倒序!!!!!
讀取第一行 1 時,跳過 G 命令,執行 h 命令將模式空間 1 復制到保持空間,執行 d 命令洗掉模式空
間的 1,
讀取第二行 2 時,模式空間是 2,執行 G 命令,將保持空間 1 追加到模式空間,此時模式空間是2\n1,執行 h 命令將 2\n1 覆寫到保持空間,d 洗掉模式空間,
以此類推,讀到第 5 行時,模式空間是 5,執行 G 命令,將保持空間的 4\n3\n2\n1 追加模式空間,
然后復制到模式空間,5\n4\n3\n2\n1,不執行 d,模式空間保留,輸出,
x 暫存空間和模式空間替換
sed -r '1h;4x;$G' passwd 把第4行替換成第1行并把第4行追加到最后一行
sed ':t;N;s/\n/,/;b t' 將換行符換成逗號

sed命令區分制表符和空格
sed -n l tab_space.txt
this is tab\tfinish.$
this is several space finish.$
awk
awk內部變數:
$0: 保存當前記錄的內容 awk -F: '{print $0}' passwd
NF: 顯示每一行的欄位數 awk -F: '{print NR,$0,NF}' passwd
NR: 顯示總共行號 awk -F: '{print NR,$0}' passwd passwd1
FNR: 分別顯示每個檔案行號 awk -F: '{print FNR,$0}' passwd passwd1
FS: 輸入欄位分隔符 默認空格 awk -F"[ :\t]" '{print $1,$2,$3}' passwd
OFS: 輸出欄位分隔符 awk 'BEGIN{FS=":";OFS="--"} {print $1,$2,$3}' passwd
RS: 輸入記錄分隔符 默認換行 awk 'BEGIN{RS="/"} {print $0}' passwd # 以"/"和空格分成多行
ORS: 輸出記錄分隔符 awk -F: 'BEGIN{ORS=" "} {print $0}' passwd # 每一行以空格分開 默認為回車
格式化輸出:
awk -F: '{print "username is: " $1 "\t uid is: " $3}' passwd # 變數以外的其他所有字串要用引號引起來
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
awk -F: '{printf "%-15s %-10s %-15s\n", $1,$2,$3}' passwd
%s 字符型別
%d 數值型別
%f 浮點型別 %.2f 保留兩位小數點
占15字符
- 表示左對齊,默認是右對齊
printf默認不會在行尾自動換行,加\n
awk模式:
-
正則運算式
(1) 匹配一整行
awk '/^root/' passwd awk '!/^root/' passwd(2) 匹配欄位
awk -F":" '$1 ~/root/' passwd awk -F":" '$1 !~/bin/' passwd -
比較運算式
awk -F":" '$3==7' passwd awk -F":" '$NF=="/sbin/nologin"' passwd awk -F":" '$NF ~"/nologin"' passwd -
條件運算式
awk -F":" '{if($3>5){print $1,$3} else{print $1}}' passwd -
算術運算:+ - * / %(模) (冪23)
awk -F":" '{if($3*1>5){print $1,$3}}' passwd -
邏輯運算子
&& 邏輯與 a&&b || 邏輯或 a||b ! 邏輯非 !aawk -F":" '$1 ~/^ro/ && $1 ~/ooot$/' passwd awk -F":" '$1 ~/^ro/ || $1 ~/^bin/' passwd awk -F":" '!($1 ~/^ro/ || $1 ~/^bin/)' passwd awk -F":" '!/^#|^$/' passwd -
布林值判斷
seq 6 |awk 'i=!i' # 列印奇數行 seq 6 |awk '!(i=!i)' # 列印偶數行 i值未被定義所以為假,!i就為真,所以i為真,列印1 第二次,i已經為真,!i為假,所以i又為假,不列印2 ,依次回圈 -
三目運算
awk 'BEGIN{print 1==1?"yes":"no"}' # 格式 條件?"真":"假" yes seq 4 |awk '{printf NR%2!=0?$0" ":$0" \n"}' # 一行拆分兩行 1 2 3 4 -
控制輸出
awk '/^root/{next}{print $0}' file # 不列印root開頭的行 seq 5 |awk 'NR!=1{print $0}' # 不列印第一行 tail -n5 /etc/services |awk '{print $2 > "a.txt"}' # 列印結果到一個檔案 tail -n5 /etc/services |awk '{print $2 |"grep tcp"}' # 過濾結果 awk 'NR%2{printf $0",";next}{print $0}' # 兩行合并為一行 ","分隔
awk腳本:
-
條件判斷
awk -F: '{if($3>0 && $3<1000){i++}} END{print i}' passwd awk -F: '{if($3==0){i++} else{j++}} END{print "管理員個數: "i; print "系統用戶數: "j}' passwd awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print "管理員: "i; print "普通用戶: "k; print "系統用戶: "j}' passwd -
回圈
awk -F: '{i=1;while(i<=5) {print $1;i++}}' passwd awk -F":" '{i=1; while(i<=NF){print $i; i++}}' passwd # 分別列印每一行的每一列 awk -F":" '/^root/{ for(i=1;i<=NF;i++) {print $i} }' passwd # 分別列印每一行的每一列 -
陣列
awk -F: '{username[i++]=$1} END{print username[0]}' passwd按索引遍歷:
awk -F: '{username[$1]++} END{for(i in username) {print i,username[i]} }' passwd # 用戶數量統計 awk -F":" '{shells[$NF]++} END{for(i in shells){print i,shells[i]}}' passwd # shell型別的數量統計 ss -antp |grep :22 |awk -F":" '!/LISTEN/{ip[$2]++} END {for (i in ip) {print i,ip[i]}}' |sort -rn -k3 |head |awk '{print $2,$3}' # 統計每個ip ssh連接的次數 awk '/22\/Mar\/2018/{ips[$1]++} END{for(i in ips){print i,ips[i]}}' access.log |awk '$2>100' |sort -k2 -rn|head # 統計nginx訪問次數大于100的ip awk '/22\/Mar\/2018/{ips[$1]++} END{for(i in ips){if(ips[i]>100){print i,ips[i]}}}' access.log |sort -k2 -rn|headcat /var/log/access_liang.log |awk '$7~/liang.php/ {print $1}' |sort |uniq -c # 統計含有liang.php頁面的ipawk '{sum += $1} END {print sum}' # 列相加 awk '{line[NR]=$0}END{for(i=NR;i>=1;i--){print line[i]}}' 1 # 行倒序 awk '{for(i=NF;i>=1;i--)if(i==1)printf $i"\n";else printf $i" "}' 1 # 列倒序統計訪問 IP 次數:
awk '{a[$1]++}END{for(v in a)print v,a[v]}' access.log統計訪問訪問大于 100 次的IP:
awk '{a[$1]++}END{for(v in a){if(a[v]>100)print v,a[v]}}' access.log統計訪問 IP 次數并排序取前10:
awk '{a[$1]++}END{for(v in a)print v,a[v] |"sort -k2 -nr |head -10"}' access.log統計時間段訪問最多的IP:
awk '$4>="[02/Jan/2018:00:00:00" && $4<="[02/Jan/2018:00:03:00"{a[$1]++}END{for(v in a)print v,a[v]}' access.log統計上一分鐘訪問量:
date=$(date -d '-1 minute' +%d/%d/%Y:%H:%M) awk -vdate=$date '$4~date{c++}END{print c}' access.log統計訪問最多的 10 個頁面:
awk '{a[$7]++}END{for(v in a)print v,a[v] |"sort -k1 -nr|head -n10"}' access.log統計每個 URL 數量和回傳內容總大小:
awk '{a[$7]++;size[$7]+=$10}END{for(v in a)print a[v],v,size[v]}' access.log統計每個 IP 訪問狀態碼數量:
awk '{a[$1" "$9]++}END{for(v in a)print v,a[v]}' access.log統計訪問 IP 是 404 狀態次數:
awk '{if($9~/404/)a[$1" "$9]++}END{for(i in a)print v,a[v]}' access.log找出 b 檔案在 a 檔案相同記錄:
awk 'NR==FNR{a[$0]=1}NR!=FNR{if($0 in a) print $0}' a b找出 b 檔案在 a 檔案不同記錄:
awk 'FNR==NR{a[$0]=1;next}!a[$0]' a b -
參考外部變數
使用-v引數可以將外部值傳給awk中使用的變數 awk -v user=root -F: '$1 == user' passwd -
案例
例1
cat a.file 姓名 費用 數量 zhangsan 8000 1 zhangsan 5000 1 lisi 1000 1 lisi 2000 1 wangwu 1500 1 zhaoliu 6000 1 zhaoliu 2000 1 zhaoliu 3000 1 awk 'NR>1{name[$1]++;number[$1]+=$3;money[$1]+=$2}END{for(i in name)print i,number[i],money[i]}' a.file zhaoliu 3 11000 zhangsan 2 13000 wangwu 1 1500 lisi 2 3000例2
求每個人的平均成績 cat a.txt one 23 23 45 22 22 two 23 23 45 22 22 28 three 23 23 45 22 22 82 23 four 23 23 45 22 22 23 45 32 23 cat a.awk BEGIN{ print "姓名","平均成績" } { for(i=2;i<=NF;i++){ sum=sum+$i } avg=sum/(NF-1) print $1,avg sum=0 } awk -f a.awk a.txt 姓名 平均成績 one 27 two 27.1667 three 34.2857 four 28.6667例3
按列統計,列轉行 cat a.file 姓名:liang 性別:male 電話:18623432212 姓名:mobai 性別:male 電話:18223432122 姓名:right 性別:female 電話:18123432212 cat a.file |awk -F":" '{print $2}' |sed "/^$/d" |awk 'BEGIN{print "姓名\t性別\t電話"} {printf NR%3!=0?$0"\t":$0"\n"}' 姓名 性別 電話 liang male 18623432212 mobai male 18223432122 right female 18123432212例4
獲取每行的最大值 cat c.txt 20 30 40 55 44 33 23 76 54 13 19 9 cat c.awk { max=$1 for (i=2;i<=NF;i++) { if ($i>max) { max=$i } } print max } awk -f c.awk c.txt 40 55 76 19 按行從小到大排序 cat c1.awk { num=0 if($1>$2) { num=$1 $1=$2 $2=num } if($1>$3) { num=$1 $1=$3 $3=num } if($2>$3) { num=$2 $2=$3 $3=num } print $1,$2,$3 } awk -f c1.awk c.txt 20 30 40 33 44 55 23 54 76 9 13 19
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/376059.html
標籤:其他
