Linux-文本三劍客
- 前言
- 一、grep
- 二、sed
- 三、awk
- 模式 -- 正則表達式
- 關系表達式、運算符表達
- 模式匹配表達式
- 動作 輸出
- 流程控制
- 參數傳遞,awk接受外部變量
- 統計
- 數組的使用
- 分組統計
- 練習
- 常用內置函數
前言
grep、sed、awk 被稱為 “文本三劍客”,它們是處理文本文件的強大工具,廣泛用于日志分析、數據提取、文本轉換等場景。三者各有側重,常結合管道(|)使用
基于行處理的文本命令
- grep 過濾文本
- sed 過濾,替換文本
- awk 截取文本,統計文本
輸出/etc/passwd文件里的uid大于1000的用戶的信息,輸入格式如下:
username: cali uid: 1001 gid: 1001
awk -F: ‘$3>1000{print “username:”$1,“uid:”$3,“gid:”$4}’ /etc/passwd
while 和 read
IFS 輸入字段分割符 input field separator
[root@hz shell]# cat uid_gid.sh
#!/bin/bash
while IFS=“:” read username x uid gid others
do
if (( uid>1000));thenecho"username:uid > 1000 ));then echo "username:uid>1000));thenecho"username:username uid: $uid gid: $gid"
fi
done </etc/passwd
[root@hz shell]# cat uid_gid_2.sh
#!/bin/bash
cat /etc/passwd|awk -F: ‘{print $1,$3,$4}’|while read username uid gid
do
if (( uid>1000));thenecho"username:uid > 1000 ));then echo "username:uid>1000));thenecho"username:username uid: $uid gid: $gid"
fi
done
截取出根分區的使用率
grep
^ 表示以什么開頭
$ 表示以什么結尾
[root@hz shell]# df |grep “/KaTeX parse error: Expected 'EOF', got '#' at position 72: …[root@hz shell]#? df |grep "/”|awk '{print KaTeX parse error: Expected 'EOF', got '}' at position 2: 5}?' 33% [root@hz …"|awk ‘{print $5}’|tr -d “%”
一、grep
grep 過濾 通用的正則表達式分析程序
grep、egrep、fgrep
做匹配來過濾的
正則表達式:
文本過濾
輸入校驗
pattern 模式 --》模板
可以接受一個正則表達式
用途:在文件中查找并顯示包含指定字符串的行
格式:grep [選項]… 模式 目標文件
-i
:查找時忽略大小寫-v
:反轉查找,輸出與模式不相符的行-n
:顯示符合模式要求的行號-r
:遞歸搜索所有文件-o
:只顯示匹配的內容-E
: 支持更多的元字符(支持擴展正則)-A
: 找到匹配行以及后幾行-B
:輸出匹配行以及前幾行
過濾掉不以#號開頭和行,過濾掉空行
[root@db ~]# egrep -v "^#|^$" sshd_config
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
DenyUsers sc
SyslogFacility AUTHPRIV
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication yes
ChallengeResponseAuthentication no
GSSAPIAuthentication yes
GSSAPICleanupCredentials no
UsePAM yes
X11Forwarding yes
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
AcceptEnv XMODIFIERS
Subsystem sftp /usr/libexec/openssh/sftp-server
統計不同狀態碼出現次數
[root@db ~]#egrep -o "HTTP/1.1\" [1-5][0-9]{2}" access.log-20250828 |sort|uniq -c21 HTTP/1.1" 20038 HTTP/1.1" 3044 HTTP/1.1" 4042 HTTP/1.1" 500
過濾出文檔中的ip地址,ip正則表達式? ipv4 [0-255].[0-255].[0-255].[0-255]
egrep "((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
過濾出/var/log/message中含有error的行,以及error信息的后5行
grep -i -A 5 'error' /var/log/messages
二、sed
sed – 編輯文本,過濾文本 非交互式 方便在腳本中使用
sed命令格式:
直接處理文本
sed [選項] 'command' file
前一個命令的輸出也可以作為sed的輸入
cat file| sed [選項] 'command'
選項
-i
直接修改文件內容-n
只打印模式匹配的行-f
將sed的命令寫入一個文件內,從腳本文件中讀取內容并執行-r
支持正則表達式-e
執行多個編輯命令時
sed常用指令 [address]command
p
– print 打印輸出模塊行!
取反 表示后面的命令對選定的行沒有作用n
next 讀取下一個輸入的行,用下一個命令處理新的行i\
insert 在當前行的上面插入文本a\
append 在當前行的后面插入文本d
delete 刪除行r
r filename 從filename讀取 ,將filename的內容放在匹配的行之后w
w filename 將匹配到的內容寫入文本N
追加下一行到當前模塊的后面s
替換操作
多條命令進行組合
- 使用 分號 ;
- 使用-e
- 還可以使用 |
address – 確定的范圍
1、空地址 – 全文處理
2、單地址 – 7p
3、步進地址 1~2, 2~2
4、地址對
N,M
從第N行到第M行N,+M
從第n行開始向下匹配M行N,/pattern/
從第n行到 pattern匹配的行 pattern支持正則表達式 使用//包裹起來/pattern1/,/pattern2/
從pattern1匹配到 pattern2 匹配的行/pattern/
匹配pattern的行$
最后一行
打印文件所有行并輸出行號
[root@db ~]# sed -n “p” test.txt
1 aaa abc
2 xxx yyy
3 ppp
4 nihao
5 hello
6 sctl world
7 balabala
8 xixi
9 haha
僅打印第7行
[root@db ~]# sed -n “7p” test.txt
7 balabala
打印所有奇數行
[root@db ~]# sed -n “1~2p” test.txt
1 aaa abc
3 ppp
5 hello
7 balabala
9 haha
[root@db ~]# sed -n “p;n” test.txt
1 aaa abc
3 ppp
5 hello
7 balabala
9 haha
打印所有偶數行
[root@db ~]# sed -n “2~2p” test.txt
2 xxx yyy
4 nihao
6 sctl world
8 xixi
[root@db ~]# sed -n “n;p” test.txt
2 xxx yyy
4 nihao
6 sctl world
8 xixi
打印1到3行
[root@db ~]# sed -n “1,3p” test.txt
1 aaa abc
2 xxx yyy
3 ppp
第 1 行開始,打印當前行 + 后續 3 行
[root@db ~]# sed -n “1,+3p” test.txt
1 aaa abc
2 xxx yyy
3 ppp
4 nihao
從第 1 行開始,打印到 “首次包含字符 s 的行”
[root@db ~]# sed -n “1,/s/p” test.txt
1 aaa abc
2 xxx yyy
3 ppp
4 nihao
5 hello
6 sctl world
打印所有包含字符串 sctl 的行
[root@db ~]# sed -n “/sctl/p” test.txt
6 sctl world
在第 7 行之后插入一行內容 this is test
[root@db ~]# sed “7 a\this is test” test.txt
1 aaa abc
2 xxx yyy
3 ppp
4 nihao
5 hello
6 sctl world
7 balabala
this is test
8 xixi
9 haha
在第 7 行之前插入一行內容 this is test
[root@db ~]# sed “7 i\this is test” test.txt
1 aaa abc
2 xxx yyy
3 ppp
4 nihao
5 hello
6 sctl world
this is test
7 balabala
8 xixi
9 haha
刪除第七行
[root@db ~]# sed “7d” test.txt
1 aaa abc
2 xxx yyy
3 ppp
4 nihao
5 hello
6 sctl world
8 xixi
9 haha
sed替換
[address]s/pattern/replacement/flags
-> 不修改原文件
- 支持正則表達式,支持分組向后引用
- & 表示已匹配的字符串
- flags: 替換標記
- g 表示行內全面替換
- p 表示打印行
- n n表示數字 1~512 表示替換第幾個
- ng 從第幾個開始替換
在第 1-5 行中,將所有字母 a(小寫)替換為 *
[root@db ~]# sed '1,5s/a/\*/g' test.txt
1 *** *bc
2 xxx yyy
3 ppp
4 nih*o
5 hello
6 sctl world
7 balabala
8 xixi
9 haha
在第 1-5 行中,將所有 a、b、c、d(小寫,連續字母范圍)替換為 *
[root@db ~]# sed '1,5s/[a-d]/\*/g' test.txt
1 *** ***
2 xxx yyy
3 ppp
4 nih*o
5 hello
6 sctl world
7 balabala
8 xixi
9 haha
直接修改要用 -i 選項
sed -i 's/old/new/g'
test.txt
使用 -i 前,建議先執行不加 -i 的命令預覽效果,確認替換正確
三、awk
awk是一種編程語言,對文本、數據進行處理
過濾、統計、截取
內部編程和c語言有相同之處
語法形式:
awk [選項] 'awk命令腳本' file
awk [選項] -f scriptfile file
常用的命令選項:
-F
指定分隔符 ,默認空白符-v
var=value 將外部變量傳遞給awk-f
scripfile 從腳本文件中讀取awk命令
awk命令腳本基本結構體,由三大塊組成
'BEGIN{語句塊} /模式/{動作} END{ 語句塊}'
BEGIN、模式通用語句塊、END 都是可選部分
- 第一部分:執行BEGIN語句塊中的內容,在處理文本之前,讀取行之前被執行。一般用在初始化變量,打印一些表頭
- 第二部分:pattern語句塊 通用語句塊,讀取文本的每一行交給這個語句塊處理
- 第三部分:讀取完所有的行之后執行, 統計、分析結果這些信息在END中完成
以a1.txt為例
[root@docker ~]# cat a1.txt
1 a1 b1 3
2 a2 b1 4
3 a3 b2 2
4 a1 b2 5
5 a2 b3 4
6 a3 b3 3
[root@docker ~]# awk 'BEGIN{print "start....";print "start2....."}{print $2}END{print "end......"}' a1.txt
start....
start2.....
a1
a2
a3
a1
a2
a3
end......
[root@docker ~]# awk 'BEGIN{print "start....";print "start2....."}{print $2}' a1.txt
start....
start2.....
a1
a2
a3
a1
a2
a3
[root@docker ~]# awk '{print $2}' a1.txt
a1
a2
a3
a1
a2
a3
[root@docker ~]# awk 'BEGIN{print "start....";print "start2....."}' a1.txt
start....
start2.....
[root@docker ~]# awk 'BEGIN{print 3/2}' a1.txt
1.5
[root@docker ~]# awk '{print 3/2}' a1.txt
1.5
1.5
1.5
1.5
1.5
1.5
[root@docker ~]# echo|awk '{print 3/2}'
1.5
[root@docker ~]# echo|awk '{print 3>2}'
[root@docker ~]# echo|awk '{print 3<2}'
0
問題:根據 awk 的邏輯判斷規則,數值比較結果會以 0(假)或 1(真) 輸出,比較結果為假輸出了0,但是比較結果為真并沒有輸出1?
通用語句塊 – 模式和動作
-
模式 – 過濾:
/正則表達式/
可以使用正則表達式匹配
關系表達式(&& || !)
、運算符表達式(> < ==)
模式匹配表達式
: 用運算符~ !~
(表示匹配或不匹配) -
動作:
動作是由一個或多個命令、表達式組成,命令之間用分號隔開- 變量或數組的賦值
- 輸出
- 使用內置函數
- 控制流語句
模式 – 正則表達式
[root@docker ~]# awk -F":" '/ss/{print}' /etc/passwd
dbus:x:81:81:System message bus:/:/sbin/nologin
sssd:x:998:998:User for sssd:/:/sbin/nologin
tss:x:59:59:Account used for TPM access:/:/usr/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/usr/share/empty.sshd:/usr/sbin/nologin[root@docker ~]# awk -F":" '/^ss/{print}' /etc/passwd
sssd:x:998:998:User for sssd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/usr/share/empty.sshd:/usr/sbin/nologin
關系表達式、運算符表達
[root@docker ~]# awk -F":" '$3>2000{print $1}' /etc/passwd
[root@docker ~]# awk -F":" '$3==2001{print $1}' /etc/passwd
[root@docker ~]# awk -F":" '$3>1010 && $3 < 2000{print $1}' /etc/passwd
[root@docker ~]# awk -F":" '$3>1010 && $3 < 2000{print}' /etc/passwd
算術運算符 + - * / % ++ –
其他運算符:
- ?: 類似于javascript的三元運算
- in
[root@docker ~]# echo |awk 'BEGIN{a="bbb";print a=="bbb"?"ok":"error"}'
ok
模式匹配表達式
[root@docker ~]# awk -F":" '$1 ~ /ss{print}' /etc/passwd
[root@docker ~]# awk -F":" '$1 ~ /^ss/{print}' /etc/passwd
# 找到家目錄不在/home 目錄下的所有用戶信息
[root@docker ~]# awk -F":" '$6 !~ /^\/home\//{print}' passwd
# 找到登錄shell為/bin/bash并且uid大于500的用戶信息
[root@docker ~]# awk -F ':' '$7=="/bin/bash" && $3>500 {print $0}' /etc/passwd
動作 輸出
內部變量:
$0
表示整行文本$1
表示分隔出來的第一列文本$n
表示分隔出來的第n列文本NF
每行$0的字段數 (最后一列可表示為$NF)NR
當前處理的行號FS
當前的輸入分隔符,默認是空白字符(空格和tab)field separatorOFS
當前的輸出分隔符,默認是空格字符(空格)output field separator
[root@docker ~]# awk '{print "最后一列為:"$NF}' a1.txt
最后一列為:end
最后一列為:4
最后一列為:2
最后一列為:end
最后一列為:4
最后一列為:3
[root@docker ~]# awk '{print "最后一列為:"$NF" "$(NF-1)}' a1.txt
最后一列為:end 3
最后一列為:4 b1
最后一列為:2 b2
最后一列為:end 5
最后一列為:4 b3
最后一列為:3 b3
[root@docker ~]# awk 'NR>3{print "最后一列為:"$NF" "$(NF-1)}' a1.txt
最后一列為:end 5
最后一列為:4 b3
最后一列為:3 b3
[root@docker ~]# awk 'BEGIN{FS=" ";OFS="**"}{print $1,$2}' a1.txt
1**a1
2**a2
3**a3
4**a1
5**a2
6**a3
[root@docker ~]# awk 'BEGIN{FS=" "}{print $1,$2}' a1.txt
1 a1
2 a2
3 a3
4 a1
5 a2
6 a3
流程控制
[root@docker ~]# echo |awk 'BEGIN{
> num=100;
> if(num>90) {
> print "大于90"
> } else {}
> }'
大于90
[root@docker ~]# echo |awk 'BEGIN{num=100;if(num>90){print "大于90"}else{print "小于等于90"}}'
大于90
參數傳遞,awk接受外部變量
1、使用雙引號
2、使用 -v
選項
3、將變量的接受放后面
[root@docker ~]# a=100
[root@docker ~]# b=200
# 1.雙引號
[root@docker ~]# echo|awk "{print $a+$b}"
300
[root@docker ~]# echo|awk '{print $a+$b}'
0
# 2.-v
# 注意沒有空格
[root@docker ~]# echo|awk -v var1=$a var2=$b '{print var1+var2}'
awk: fatal: cannot open file `{print var1+var2}' for reading: No such file or directory
[root@docker ~]# echo|awk -v var1=$a var2=$b'{print var1+var2}'
300
# 3.變量放后面
# 注意空格
[root@docker ~]# echo|awk '{print var1+var2}'var1=$a var2=$b
200
[root@docker ~]# echo|awk '{print var1+var2}' var1=$a var2=$b
300
統計
求取a1.txt文件中,第四列的平均值、最大值、最小值
[root@docker ~]# awk 'NR==1{max=$4} {max=($4>max)?$4:max} END{print max}' a1.txt
5
[root@docker ~]# awk 'NR==1{min=$4} {min=($4<min)?$4:min} END{print min}' a1.txt
2
[root@docker ~]# awk 'BEGIN{sum=0;count=0}{sum+=$4;count+=1}END{print sum/count}' a1.txt
3.5
數組的使用
分析、統計
分類聚合
類似python中的字典
arr[1]=“aa”
arr[2]=“bb”
arr[‘a’] = 1 #k-v存儲 key-value
arr[‘b’] = 2
讀取數組
{ for (i in arr){print arr[i]}}
{ for (i=1;i<=len;i++) {print arr[i]}}
# 沒有相應的索引,也可以取值為空
[root@docker ~]# echo |awk 'BEGIN{t1["a"]=1;t2["b"]=2}{if(t1["a"]==1){print "ok"}}'
ok
[root@docker ~]# echo |awk 'BEGIN{t1["a"]=1;t2["b"]=2}{if(t1["a"]==1){print t1["c"]}}'[root@docker ~]# echo |awk 'BEGIN{info["name"]="sc"; info["age"]=7}{print info["name"]}'
sc
[root@docker ~]# echo |awk 'BEGIN{info["name"]="sc"; info["age"]=18}{print info["age"]}'
18
[root@docker ~]# echo |awk 'BEGIN{info["name"]="sc"; info["age"]=7}{print info["sex"]}'# 判斷屬組中有沒有這個key -- in
[root@docker ~]# echo | awk 'BEGIN{info["name"]="sc";info["age"]=18}{if("name" in info){print "yes"}}'
yes
分組統計
[root@docker ~]# cat stu.txt
工號 姓名 性別 部門 工資
1 張三 女 SA 8000
2 李四 男 PE 7000
3 王二 女 SA 9000
4 小李 男 PE 8000
5 小張 女 PE 9000
6 小謝 男 SA 9000
7 小米 男 SA 8000
5 小陳 女 PE 7000
[root@docker ~]# awk 'NR>1{ stuff[$3]+=$4 } END{ for(i in stuff) {print i"-->"stuff[i] }}' stu.txt
男-->0
女-->0
[root@docker ~]# awk 'NR>1{ stuff[$3]+=$5 } END{ for(i in stuff) {print i"-->"stuff[i] }}' stu.txt
男-->32000
女-->33000# 統計每個部門,每個性別 各發出去多少工資 -- 按什么分組,就以什么作為key
[root@docker ~]# awk 'NR>1{ stuff[$3"--"$4]+=$5 } END{ for(i in stuff) {print i"-->"stuff[i] }}' stu.txt
女--SA-->17000
女--PE-->16000
男--SA-->17000
男--PE-->15000
練習
# 統計本機各個連接狀態的數量
netstat -anplt|awk 'NR>2{ state[$6]++} END{ for(i in state) {print i"-->"state[i] }}'
LISTEN-->27
ESTABLISHED-->70# 降序統計客戶端 IP 的訪問次數
cat access.log | awk '{print $1}'|sort|uniq -c|sort -rn# 降序統計 Referer 中含 IP 地址的訪問次數
cat access.log | awk '{print $11}'|sort|uniq -c|egrep "((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"|sort -rn# 統計nginx訪問日志的每分鐘,每個路徑訪問次數
awk -F"[ :]" '{count[$4$5"--"$10]++}END{for(i in count){print i":"count[i]}}' access.log
常用內置函數
算術相關: int(x)
rand()
0<=n<1sqrt()
開平方
字符串相關:
length(string)
長度split(string, A,[指定的分隔符])
切割字符串substr(string, M, [N])
對string進行截取,從M的位置開始,截取N個
統計access.log日志中,每個小時,每個路徑訪問次數
awk '{count[substr($4,2,14)"--"$7]++}END{for(i in count){print i":"count[i]}}' scwebsite2_ssl.log