1. grep:基于正则表达式查找满足条件的行
1.1 内容检索
注:pattern代表正则表达式,所以需要懂正则表达式
- 获取行:grep pattern file
- 获取内容:grep -o pattern file
- 获取上下文:grep -A -B -C pattern file -A和-B后面需要一个数字
[jck253509@shell.ceshiren.com shell]$ cat grep_test
a, 1, 20
a, 2, 20
a, 3, 20
b, 1, 1
b, 2, 2
b, 3, 3
c, 1, 21
c, 2, 30
c, 3, 89
[jck253509@shell.ceshiren.com shell]$ grep a grep_test
a, 1, 20
a, 2, 20
a, 3, 20
[jck253509@shell.ceshiren.com shell]$ grep 1 grep_test
a, 1, 20
b, 1, 1
c, 1, 21
[jck253509@shell.ceshiren.com shell]$ grep ", 1, " grep_test
a, 1, 20
b, 1, 1
c, 1, 21
[jck253509@shell.ceshiren.com shell]$ grep "1$" grep_test
b, 1, 1
c, 1, 21
[jck253509@shell.ceshiren.com shell]$ grep -o "1$" grep_test
1
1
[jck253509@shell.ceshiren.com shell]$ grep -o "[0-9]*1$" grep_test
1
21
[jck253509@shell.ceshiren.com shell]$ grep -A2 -B2 "2," grep_test
a, 1, 20
a, 2, 20
a, 3, 20
b, 1, 1
b, 2, 2
b, 3, 3
c, 1, 21
c, 2, 30
c, 3, 89
[jck253509@shell.ceshiren.com shell]$ grep -A1 -B1 "2," grep_test
a, 1, 20
a, 2, 20
a, 3, 20
b, 1, 1
b, 2, 2
b, 3, 3
c, 1, 21
c, 2, 30
c, 3, 89
[jck253509@shell.ceshiren.com shell]$ grep -B1 "2," grep_test
a, 1, 20
a, 2, 20
--
b, 1, 1
b, 2, 2
--
c, 1, 21
c, 2, 30
1.2 文件检索:检索哪个文件里有那些内容
- 递归搜索:grep pattern -r dir/
- 展示匹配文件名:grep -H 111 /tmp/1
- 只展示匹配文件名:grep -1 111 /tmp/1
1.3 范围约束
- 忽略大小写:grep -i pattern file
- 不显示匹配的行(不匹配的行输出):grep -v pattern file
- 使用扩展正则表达式:grep -E pattern file
- 文件范围和目录范围约束:grep 111 -r /tmp/demo/ --include “11*”
1.4 进程检索
- 进程检索场景比较特殊,需要注意
- grep本身会开启新进程,所以需要单独过滤掉grep进程
[jck253509@shell.ceshiren.com ~]$ ps -ef | grep ssh
root 4281 15082 0 12:22 ? 00:00:00 sshd: jck253509 [priv]
jck2535+ 4305 4281 0 12:23 ? 00:00:00 sshd: jck253509@pts/8
root 6973 15082 0 12:51 ? 00:00:00 sshd: jck253509 [priv]
jck2535+ 6978 6973 0 12:51 ? 00:00:00 sshd: jck253509@pts/9
jck2535+ 7558 6979 0 12:59 pts/9 00:00:00 grep --color=auto ssh
root 10330 15082 0 2021 ? 00:00:00 sshd: quanjinlong [priv]
quanjin+ 10332 10330 0 2021 ? 00:00:00 [sshd] <defunct>
root 15082 1 0 2021 ? 00:02:35 /usr/sbin/sshd -D
root 25602 15082 0 10:28 ? 00:00:00 sshd: lg48968116 [priv]
lg48968+ 25606 25602 0 10:28 ? 00:00:00 sshd: lg48968116@pts/0
root 26462 15082 0 08:40 ? 00:00:00 sshd: ck245138 [priv]
ck245138 26527 26462 0 08:40 ? 00:00:00 sshd: ck245138@pts/7
root 29546 15082 0 11:02 ? 00:00:00 sshd: 61993657 [priv]
61993657 29561 29546 0 11:02 ? 00:00:00 sshd: 61993657@pts/4
[jck253509@shell.ceshiren.com ~]$ ps -ef | grep ssh | grep -v grep
root 4281 15082 0 12:22 ? 00:00:00 sshd: jck253509 [priv]
jck2535+ 4305 4281 0 12:23 ? 00:00:00 sshd: jck253509@pts/8
root 6973 15082 0 12:51 ? 00:00:00 sshd: jck253509 [priv]
jck2535+ 6978 6973 0 12:51 ? 00:00:00 sshd: jck253509@pts/9
root 10330 15082 0 2021 ? 00:00:00 sshd: quanjinlong [priv]
quanjin+ 10332 10330 0 2021 ? 00:00:00 [sshd] <defunct>
root 15082 1 0 2021 ? 00:02:35 /usr/sbin/sshd -D
root 20325 15082 0 Oct23 ? 00:00:00 sshd: yangchenhui [priv]
yangche+ 20328 20325 0 Oct23 ? 00:00:00 sshd: yangchenhui@pts/12
root 25602 15082 0 10:28 ? 00:00:00 sshd: lg48968116 [priv]
lg48968+ 25606 25602 0 10:28 ? 00:00:00 sshd: lg48968116@pts/0
root 26462 15082 0 08:40 ? 00:00:00 sshd: ck245138 [priv]
ck245138 26527 26462 0 08:40 ? 00:00:00 sshd: ck245138@pts/7
root 29546 15082 0 11:02 ? 00:00:00 sshd: 61993657 [priv]
61993657 29561 29546 0 11:02 ? 00:00:00 sshd: 61993657@pts/4
1.5 man grep …
参考:Linux基本命令
2. awk:根据定位到的数据行处理其中的分段
2.1 awk的基本语法
- awk是linux下的一个命令,同时也是一种语言解析引擎
- awk具备完整的编程特性。比如执行命令,网络请求等
- 精通awk,是一个linux工作者的必备技能
- 语法awk ’pattern{action}‘
2.2 awk上下文变量
- 开始BEGIN结束END
- 行数NR
- 字段与字段数$1$2$3… $NF NF
- 整行 $0
- 字段分隔符 FS
- 输出数据的字段分隔符 OFS
- 记录分割符 RS
- 输出字段的行分隔符ORS
2.3 字段变量用法
- -F参数指定字段分隔符,可以用|指定多个-多分隔符-F’<|>’
- BEGIN{FS=“_”}也可以表示分隔符
- $0代表当前的记录,即整行
- $1代表第一个字段
- $N代表第N个字段
- $NF代表最后一个字段
- $(NF-1)代表倒数第二个字段
2.5 pattern表达式
- 正则匹配 $1~/pattern/ /pattern/
- 比较表达式 $2>2 $1==“b”
2.6 awk pattern 匹配表达式案例
- 开始和结束 awk ‘BEGIN{}END{}’
- 正则匹配
- 整行匹配 awk ‘/Running/’
- 字段匹配 awk ‘$2~/xxx/’
- 行数表达式
- 取第二行 awk ‘NR==2’
- 去掉第一行 awk ‘NR>1’
- 区间选择
- awk ‘/aa/, /bb/’
- awk ‘/1/, NR==2’
2.7 action表达式{action}
- 打印:{print $0} {print $2}
- 赋值 {$1=“abc”}
- 处理函数
- 原始内容$0
- 更新后内容{$1=$1;print $0}
2.8 计算平均数
看下面bash
2.9 awk的词典结构array
- array是稀疏矩阵,类似python的词典类型
- 统计多家机构的营业额:看下面的bash
- 统计多家机构的营业额平均值:看下面的bash
[jck253509@shell.ceshiren.com shell]$ echo '1
> 2
> 3
> 4' | awk 'NR==2'
2
[jck253509@shell.ceshiren.com shell]$ echo '1
2
3
4' | awk '$1~/3/'
3
[jck253509@shell.ceshiren.com shell]$ echo '1
2
3
4' | awk '/3/'
3
[jck253509@shell.ceshiren.com shell]$ echo '1
2
3
4' | awk '/3/{print $0}'
3
[jck253509@shell.ceshiren.com shell]$ echo '1
2
3
4' | awk 'NR>2'
3
4
[jck253509@shell.ceshiren.com shell]$ echo '1
2
3
4' | awk '/1/,/2/'
1
2
[jck253509@shell.ceshiren.com shell]$ echo '1
2
3
4' | awk '/1/,/3/'
1
2
3
[jck253509@shell.ceshiren.com shell]$ echo 1:2:3 | awk 'BEGIN{RS=":"}{print $0}'
1
2
3
[jck253509@shell.ceshiren.com shell]$ echo '1
2
3' | awk 'BEGIN{RS="";FS="\n";OFS=":"}{$1=$1;print $0}'
1:2:3
[jck253509@shell.ceshiren.com shell]$ echo '1
2
3' | awk 'BEGIN{ORS=":"}{$1=$1;print $0}'
1:2:3:
[jck253509@shell.ceshiren.com shell]$ echo '1,20
> 2,20
> 3,30' | awk 'BEGIN{total=0;FS=","}{total+=$2}END{print total/NR}'
23.3333
[jck253509@shell.ceshiren.com shell]$ echo 'a, 1, 10
a, 2, 20
> a, 3, 30
> b, 1, 5
> b, 2, 6
> b, 3, 7' | awk '{data[$1]+=$3}END{for(k in data) print k, data[k]}'
a, 60
b, 18
[jck253509@shell.ceshiren.com shell]$ echo 'a, 1, 10
a, 2, 20
a, 3, 30
b, 1, 5
b, 2, 6
b, 3, 7' | awk '{data[$1]+=$3;count[$1]+=1;}END{for(k in data) print k, data[k]/count[k]}'
a, 20
b, 6
3. sed:stream editor 定位并修改数据
3.1 sed基本语法与常用参数
- 语法结构 sed [addr]X[options]
- -e表达式
- sed -n ‘2p’ 打印第二行
- sed ‘s#hello#world#’ 修改
- -i 直接修改源文件
- -E扩展表达式
- -debug调试
3.2 sed pattern表达式
- 行数与行数范围 20,30,35
- 正则匹配 /pattern/
- 区间匹配 //,//
3.3 sed action表达式
- p打印,通畅结合-n参数:sed -n ‘2p’
- s查找替换:s/REGEXP/REPLACEMENT/[FLAGS]
- d删除,删除前两行 sed ‘1, 2d’
- a追加
- c改变
- i插入内容到匹配行之前
# -i在匹配行前加上123
[jck253509@shell.ceshiren.com shell]$ echo abd | sed '/d/i 123'
123
abd
# -a在匹配行后加上123
[jck253509@shell.ceshiren.com shell]$ echo abd | sed '/d/a 123'
abd
123
# -c将匹配的内容行修改为123
[jck253509@shell.ceshiren.com shell]$ echo abd | sed '/d/c 123'
123
- e执行命令
- 分组匹配到字段提取:sed ‘s#([0-9])|([a-z])#\1\2#’
[jck253509@shell.ceshiren.com shell]$ cat grep_test
a, 1, 20
a, 2, 20
a, 3, 20
b, 1, 1
b, 2, 2
b, 3, 3
c, 1, 21
c, 2, 30
c, 3, 89
# 打印第二行
[jck253509@shell.ceshiren.com shell]$ sed -n '2p' grep_test
a, 2, 20
# 打印2-4行
[jck253509@shell.ceshiren.com shell]$ sed -n '2,4p' grep_test
a, 2, 20
a, 3, 20
b, 1, 1
# 打印匹配到的b
[jck253509@shell.ceshiren.com shell]$ sed -n '/b/p' grep_test
b, 1, 1
b, 2, 2
b, 3, 3
3.4 行数操作
- 打印特定行:sed -n 2p
- 删除最后一行:sed $d
3.5 s表达式
- s表示替换:
echo a:b:c
| sed ‘s/:/123&/’ - s后面表示的追加字符可以为任意字符:
echo a:b:c | sed 's/:/&123/'
- g表示全局匹配:
echo a:b:c | sed 's#:#|#g'
- &表示匹配内容
# 删除最后一行 $表示最后一行
[jck253509@shell.ceshiren.com shell]$ sed '$d' grep_test
a, 1, 20
a, 2, 20
a, 3, 20
b, 1, 1
b, 2, 2
b, 3, 3
c, 1, 21
c, 2, 30
# 删除前三行
[jck253509@shell.ceshiren.com shell]$ sed '1,3d' grep_test
b, 1, 1
b, 2, 2
b, 3, 3
c, 1, 21
c, 2, 30
c, 3, 89
# 在第一个匹配内容的前面加123
[jck253509@shell.ceshiren.com shell]$ echo a:b:c | sed 's/:/123&/'
a123:b:c
# 在第一个匹配内容的后面加123
[jck253509@shell.ceshiren.com shell]$ echo a:b:c | sed 's/:/&123/'
a:123b:c
# 将所有的:修改为| g表示全局修改
[jck253509@shell.ceshiren.com shell]$ echo a:b:c | sed 's#:#|#g'
a|b|c
# 将a修改为great_a
[jck253509@shell.ceshiren.com shell]$ sed 's/a/great_a/' grep_test
great_a, 1, 20
great_a, 2, 20
great_a, 3, 20
b, 1, 1
b, 2, 2
b, 3, 3
c, 1, 21
c, 2, 30
c, 3, 89
# 将a修改为great_a,可以使用/表示 也可以使用#表示
[jck253509@shell.ceshiren.com shell]$ sed 's#a#great_a#' grep_test
great_a, 1, 20
great_a, 2, 20
great_a, 3, 20
b, 1, 1
b, 2, 2
b, 3, 3
c, 1, 21
c, 2, 30
c, 3, 89
# 将所有以1结尾的数修改为100
[jck253509@shell.ceshiren.com shell]$ sed 's#1$#100#' grep_test
a, 1, 20
a, 2, 20
a, 3, 20
b, 1, 100
b, 2, 2
b, 3, 3
c, 1, 2100
c, 2, 30
c, 3, 89
# 将每行第一个匹配到的内容修改为100
[jck253509@shell.ceshiren.com shell]$ sed 's#1#100#' grep_test
a, 100, 20
a, 2, 20
a, 3, 20
b, 100, 1
b, 2, 2
b, 3, 3
c, 100, 21
c, 2, 30
c, 3, 89
# 将每行第一个匹配到的内容修改为9
[jck253509@shell.ceshiren.com shell]$ sed 's#1#9#' grep_test
a, 9, 20
a, 2, 20
a, 3, 20
b, 9, 1
b, 2, 2
b, 3, 3
c, 9, 21
c, 2, 30
c, 3, 89
# 将所有的1修改为9
[jck253509@shell.ceshiren.com shell]$ sed 's#1#9#g' grep_test
a, 9, 20
a, 2, 20
a, 3, 20
b, 9, 9
b, 2, 2
b, 3, 3
c, 9, 29
c, 2, 30
c, 3, 89
# 将所有匹配到的内容前加123
[jck253509@shell.ceshiren.com shell]$ echo a:b:c | sed 's/:/123&/g'
a123:b123:c
# 将所有匹配到的内容前加12,后加3
[jck253509@shell.ceshiren.com shell]$ echo a:b:c | sed 's/:/12&3/g'
a12:3b12:3c
3.6 反向引用
- 使用()对数据进行分组
- 使用\1\2反向引用分组
# ([1-3])定位到一个1-3的数字,-E表示扩展正则
[jck253509@shell.ceshiren.com shell]$ echo 0 1 2 3 4 | sed -E 's#([1-3]) ([1-3]) ([1-3])#\3 \2 \1#'
0 3 2 1 4
4. 匹配管道使用:shell管道piping
4.1 程序运行环境输入与输出
- 标准输入0:
read a; echo $a
- 标准输出1:
echo ceshiren.com
- 错误输出2:
ls not_exist_dir
[jck253509@shell.ceshiren.com shell]$ read a;
9090
[jck253509@shell.ceshiren.com shell]$ echo $a
9090
[jck253509@shell.ceshiren.com shell]$ ls not_exist_dir
ls: cannot access not_exist_dir: No such file or directory
4.2 管道重定向
- 管道与管道之前可以重定向
- 管道与文件之前可以重定向
# 从文件中读取一行,存储到变量a中
[jck253509@shell.ceshiren.com shell]$ read a < grep_test
# 打印变量a
[jck253509@shell.ceshiren.com shell]$ echo $a
a, 1, 20
# 将内容输入到文件中,文件内容被修改
[jck253509@shell.ceshiren.com shell]$ echo 111 > grep_test
[jck253509@shell.ceshiren.com shell]$ cat grep_test
111
# 将错误信息输出到文件中 2>&1将标准错误输出重定向到标准错误输出中
[jck253509@shell.ceshiren.com shell]$ ls not_exist_file > grep_test 2>&1
[jck253509@shell.ceshiren.com shell]$ cat grep_test
ls: cannot access not_exist_file: No such file or directory
4.3 管道连接符 |
- 管道连接符 | 可以连接多个程序的执行
- 管道连接是以子进程的方式启动的
# 输入和输出使用管道连接
[jck253509@shell.ceshiren.com shell]$ echo hogwarts | { read line; echo input is $line;}
input is hogwarts
# curl访问一个网站, {"id[^}]*}表示匹配从"id往右的第一个}
[jck253509@shell.ceshiren.com shell]$ curl https://ceshiren.com/categories.json \
| grep -o '{"id[^}]*}' \
| awk -F, '{print $2, $6}' \
| awk -F '"' '{print $7, $4}' \
| sed 's#:##' \
| sort -nr \
| head -5
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 13227 0 13227 0 0 44709 0 --:--:-- --:--:-- --:--:-- 44837
1622 霍格沃兹答疑区
619 职位内推
535 开源项目
301 测试开发
201 霍格沃兹测试开发学社
4.4 管道执行的上下文控制
- 使用{command;}注意花括号与内部命令之间的空格与分号
- 使用控制逻辑while read组合
- 使用$()"
echo $a
# 这个方式无法获得变量x
echo hello world | read x; echo $x
# 如下两个方式可以获得变量x
echo hello world | { read x; echo $x;}
echo hello world | while read x; do echo $x; done
4.5 平台实现差异与官方文档
- mac上的实现与gnu的实现有差异
- 不同版本之前有小的语法差异
- 可以通过独立下载gawk等工具实现
- 官方文档
- https://www.gnu.org/software/grep/manual/grep.html
- https://www.gnu.org/software/gawk/manual/gawk.html
- https://www.gnu.org/software/sed/manual/sed.html
4.6 BRE基本正则表达式
- ^开头$结尾
- [a-z][0-9]区间,如果开头带有^表示不能匹配区间内的元素
- *0个或多个
- . 表示任意字符
4.7 BRE扩展正则表达式
- 基本正则表达式(BRE)基础上的扩展
- ?非贪婪匹配,匹配前面的子表达式零次或一次
- +一个或者多个
- ()分组
- {}范围约束
- | 匹配多个表达式的任何一个
echo 123 | grep -E "1|3"
sed -E -e "s#1|3#x#g"
echo 123 | awk '/4|3/'
5. Nginx日志分析实战
5.1 找出log中的404和500的报错
# 使用grep实现
less nginx.log | grep -E '(" 404 |" 500 )'
# 使用awk实现
less nginx.log | awk '$9~/404|500/{print $0}' 或者
less nginx.log | awk '$9~/404|500/' 或者
less nginx.log | awk '$9==404 || $9==500/'
5.2 找出访问量最高的ip,统计分析,取出top3的ip和数量
less nginx | awk 'print $1' | sort | uniq -c | sort -nr | head -3
或者
awk '{print $1}' nginx.log | sort | uniq -c | sort -nr | awk 'NR<4'
6 性能及网站统计
6.1 统计一个进程的实时cpu数据需要用到哪个命令
# 关注进程
# STIME开始运行时间 TTY是否有交互 TIME运行时长 CMD最后执行的命令
[jck253509@shell.ceshiren.com ~]$ ps -ef | head -3
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 2020 ? 10:01:02 /usr/lib/systemd/systemd --system --deserialize 21
root 2 0 0 2020 ? 00:00:08 [kthreadd]
# 关注性能
[jck253509@shell.ceshiren.com ~]$ ps aux | head -5
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 191120 3148 ? Ss 2020 601:02 /usr/lib/systemd/systemd --system --deserialize 21
root 2 0.0 0.0 0 0 ? S 2020 0:08 [kthreadd]
root 3 0.0 0.0 0 0 ? S 2020 7:56 [ksoftirqd/0]
root 5 0.0 0.0 0 0 ? S< 2020 0:00 [kworker/0:0H]
# 自定义ps
ps_ex(){ ps -o uname,pid,ppid,thcount,ni,pri,psr,pcpu,rss,vsz,sz,start_time,time,comm,c,command,args,"$@";}
}
ps打印的是cpu快照,不是实时的cpu,不可以看性能指标;
top打印的是实时的,可以看性能指标
6.2 统计一个进程的性能:根据pid打印cpu和mem
# 输出20s内某个进程的每秒的cpu和mem,并最后空出一行统计平均性能
top -p 22985 -b -d 1 -n 20 | grep --line-buffered -i aliyun | grep -i dun
top -b -d 1 -n 20 | grep --line-buffered -i yundun$ | awk 'BEGIN{print "cpu", "mem"}{cpu+=$9;mem+=$10; print $9,$10}END{print "";print cpu/NR, mem/NR}'
6.3 网络命令
netstat -tlnp
netstat -tnp
- mac与linux不一致:
netstat -p tcp -n -a
# 统计链接22端口的ip分布情况
netstat -tn | awk 'NR>2{ print $4,$6 }' | awk -F: '{ print $2 }' | sort | uniq -c | sort -nr | awk '{ print $2"\t"$3"\t"$1}'
[jck253509@shell.ceshiren.com shell]$ connect(){ netstat -tn | awk 'NR>2{ print $4,$6 }' | awk -F: '{ print $2 }' | soiq -c | sort -nr | awk '{ print $2"\t"$3"\t"$1}'; }
[jck253509@shell.ceshiren.com shell]$ type connect
connect is a function
connect ()
{
netstat -tn | awk 'NR>2{ print $4,$6 }' | awk -F: '{ print $2 }' | sort | uniq -c | sort -nr | awk '{ print $2"\t"$3"\t"$1}'
}
注意点:
1、grep和正则玩
[root@moli tmp]# grep -nvE r+ b.txt // grep和扩展正则玩的时候 需要加上-E
4:dchjdewi
2、双引号和单引号的区别
- 双引号会对字符串转义 # a=10 echo “$a” 打印10
3、练习 awk + grep + sed
# 1、日志操作:找出log中404 500的报错条数
grep -E "\s404\s | \s500\s" nginx.log | wc -l
awk '$9~/404|500/' nginx.log | wc -l
# 2、awk 支持扩展正则 不需要 +E
# 3、访问量最高的ip
awk '{print $1}' nginx.log | sort | uniq -c | sort -nr | head -3
cat access.log/ | grep -o '^[0-9]*.[0-9]*.[0-9]*.[0-9]*' | sort | uniq -c | sort -rn | head -3
# 4、将topics后面的数字替换成number
grep '/topics/' nginx.log | sed 's@/topics/[0-9]*@/topics/number@'
# 5、将ip地址横向打印
awk '{print $1}' nginx.log | sed ':1;N;s/\n/|/;t1'
# :1和t1是一个组合,代表打一个标记,t1就是跳转到标记处(对命令打标记)
# 6、sed 中的 / 可以使用 # 或 @ 替换