前言



工作了好几年,也积攒了大量的脚本,有需求了改改旧脚本就可以了。对于以前没写过的脚本,比如想完成一个新需求,而某些参数可以满足的情况,网上搜索后,加上就行了,有什么困难的呢?所以对 “我” 个人来说,只有 “我” 有需要的参数对 “我” 才是有价值的,并且大多数的人实际上并不会用到除了常用的几个参数的其他功能,就是说很多功能它设计出来有,但是实际在大部分的生产环境中人们并不会用到。
对于一个实用主义来说,我希望当有某种需求可以直接看到有解决这个需求的方法,所以趁着忽然有想法,写一个需求和对应命令的集合,列出sed awk grep中最实用的命令
(那种各类教程中常出现并演示,但实际很少或并不会用到的命令,这里基本不会总结。就是说没有实际需求或需求特别少的不写,就写实用和特殊的用法,原理这里基本也不过多解释)



sed



打印或截取



常用于日志的截取分析
sed默认会输出所有文本内容,使用-n参数后只显示处理过的行



<1>打印从某个匹配内容到某个匹配内容的行
p:打印出匹配的内容,常与n连用

截取日志,哪个时间段到哪个时间段

sed -n '/2021:14/,/2021:20/p' access.log

sed -n '/28\/Apr\/2021:14/,/28\/Apr\/2021:20/p' access.log |awk '{print $1}' |sort | uniq -c |wc -l


<2>打印某一个字段到文本最末尾的内容

sed -n "/hello/,//p" test.txt

sed  -n  "/${binlog_name}/,//p" binlog.txt


<3>打印出匹配内容的行

sed -n '/hostname/p' test.txt 

sed -n '/^hostname/p'  test.txt


grep -rn hostname test.txt

grep -rn "^hostname"  test.txt

grep命令与对应sed命令的结果相同,但是更常用的是grep。因为grep还能从多个文件中查找匹配的内容,当你执意查某个字段的时候很多的时候是因为你不知道它在哪个文件中,用grep可以批量查找该字段在哪个文件哪一行

grep -rn hostname ./script/


<4>统计文件有多少行

sed -n '$='  test.txt
比它更常用的实际是wc统计,上面的sed的原理是列出最后一行的行号
cat test.txt | wc -l


<5>打印文件的最后一行

sed -n '$p' test.txt 
属于习惯的问题,从尾部查看日志或文件还是tail更常用

tail -n1 test.txt


<6>打印某一行到某一行

打印第3到6行
sed -n '3,6p' test.txt 
打印第3到最后一行
sed -n '3,$p' test.txt

指定打印某一行到某一行,这是head和tail不具备的,通常有这个指定行数打印的需求一般是已经知道大概的内容了,是用sed来进行更精准的截取



<7>匹配非注释行和非空白行

sed -n "/^\s*[^# \t].*$/p" test.txt
grep -v ^# test.txt
grep -v ^$ test.txt
cat test.txt | grep -v ^# |grep -v ^$
grep -Ev '^$|#' test.txt

egrep -v "^$|^#" test.txt

这个需求通常是用于对配置文件的处理,使配置看起来更明了
从上方的示例就能看出grep的优势,在这个需求下grep比sed更常用,字符越多越容易出错



删除与替换



-i:直接对内容进行修改(不加-i时只是预览,不会对文件做实际修改)
d:删除匹配的内容



<1>删除指定行

删除第4行
sed -i '4d' test.txt

删除第1~2行
sed -i '1,2d' test.txt 

删除1~2之外的所有行
sed -i '1,2!d' test.txt 

删除最后一行
sed -i '$d' 1.txt 

删除匹配字段的行
sed -i '/code/d' test.txt

删除从匹配字段的行到最后一行
sed -i '/code/,$d' test.txt 

无论在什么系统,删除操作总是需要注意的,如果不知道或不明确要删除的内容或者这一字段、这一行的作用,最好不要加-i
实际上还有很多花里胡哨的删除用法,不是用不到,只是正常生产活动很少用它,用这个的人必须心里有数,知道自己删了什么,会造成什么后果,删除用的花里胡哨恢复的时候越狼狈
这个删除的需求常用来删改配置文件,但是通常工作人员删除某条配置一般是进入文件中查看再删除,确保没有删错。即使是脚本中出现sed也是常用于替换而不是删除,只有很少的是删除某条配置再添加。当然常用sed来删除内容的也有,部分病毒脚本以此删除指定历史记录和系统日志。



<2>删除文件中的空格

删除字符串行首空格(删除文件中首部空格)
sed -i 's/^[ \t]*//g' test.txt

删除字符串行尾空格(删除文件中尾部空格)
sed -i 's/[ \t]*$//g'  test.txt

去除字符串中所有空格(去除字符串中所有空格)
sed -i 's/[[:space:]]//g'  test.txt

替换文件中注释的行为空行,然后删除空行(多个操作用分号隔开)
sed -i 's/^#.*//;/^$/d' test.txt
可拆分为如下2句
sed -i 's/^#.*//g' test.txt
sed -i "/^$/d" test.txt


删除文件中的某个符号('"'封号   ','逗号)
sed -i 's/"//g' test.txt
sed -i 's/,//g' test.txt


看似删除实际上是替换的用法,将某些内容替换成了空。常用来清除某些文件中空格,处理因空格问题导致的格式错误。不确认要删除,只是想预览一下,不要加-i



<3>替换匹配的字段为其他字段
-r:可使用正则表达式

将匹配的字段替换成其他字段
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config

将匹配字段的整行替换成其他字段
sed -r -i '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config

第一条个是sed中最常用的用法,常用来配置文件,对指定的某个字段或参数进行替换。常用来以模版文件为基础实时替换参数生成新配置文件



<4>将某个符号替换成其他符号

将逗号替换成换行
sed 's/,/\n/g'  test.txt

将;替换成换行
sed 's/;/\n/g'  test.txt

将//替换成换行
sed 's#//#\n#g'  test.txt
sed 'sA//A\nAg'  test.txt
(sed可以其他符号为分隔符,不一定用/,但是分隔符要避免与替换的内容一致,演示如下)
这个是以字母A为分隔符,将http替换成https,这个是成功的
sed 'sAhttpAhttpsAg'  test.txt
这个是以字母A为分隔符,将AttpA替换成tAps或Attp替换成AtAps,你自己都不知道替换什么,系统更不知道就会出现报错
sed 'sAAttpAAtApsAg'  test.txt


将每一行的换行替换成以(",")结尾,并变成一行
sed ':label;N;s/\n/","/;b label' test.txt

将每一行的换行替换成以(,)结尾,并变成一行
sed ':label;N;s/\n/,/;b label' test.txt

替换成换行的用法常用来将文件中以某符号为连接的内容,替换成一行一行,可用于后续的去重和统计(例如将 wang,zhang,li,zhao一条内容替换成一行一行展示)

将换行变成一行的用法,常用来做数据和文本的处理,生成指定的格式



插入



<1>插入某个字符到某个位置

向第三行下面添加hello,hello变成了第4行
sed '3ahello' test.txt

向指定字段后添加world(有多行包含该字段,则包含指定字段的每一行之后都会插入hello)
sed '/hello/aworld' test.txt

向最后一行添加hello,hello变成了最后一行
sed '$ahello' test.txt

向第三行前面添加hello,hello变成了第3行
sed '3ihello' test.txt

向指定字段的前方添加hello(有多行包含该字段,则包含指定字段的每一行之前都会插入hello)
sed '/world/ihello' test.txt

在每行的行首添加字符
sed 's/^/world&/g' test.txt

在第5的行首添加字符
sed '5{s/^/world/}' test.txt 

在5-8行的行首添加注释
sed '5,8{s/^/#/}' test.txt

取消全文注释
sed 's/^#//g' test.txt

取消1到4行的注释
sed  '1,4s/^/#/g' test.txt

在每行的行尾添加字符
sed 's/$/&world/g' test.txt

在指定行的行尾添加字符(示例是第5行)
sed '{5/$/world/}' test.txt

插入的用法最常见的还是对注释的批量添加和删除
插入这种用法不适合写入脚本,因为每插入一次都会多出一些字段,除非还得判断是否已插入过,所以脚本中更常见的是替换的用法



补充



显示行号或给文本添加行号
sed = test.txt | sed 'N;s/\n/\t/'


sed -n '=' test.txt > tmp
paste test.txt tmp > result.txt


awk '{print NR,$0}' test.txt 

这个需求常用于对文本的处理。首先 生成带行号的内容,后续依据行号进行处理
对于这个需求,awk更实用



awk



最常出现的地方是对日志的分析,打印某些字段后统计

主要功能还是打印需求的字段后处理 或者 打印处理后的字段,而除此之外使用方式并不常见到,就只分为基础和特殊用法两部分



基础用法



打印部分内容

打印文件的第一列
awk '{print $1}' test.txt

打印文件的第一,第二列
awk '{print $1,$2}' test.txt

打印文件的最后一列
awk '{print $NF}' test.txt

打印文件的第几行(NR是指行号)
awk 'NR==1{print}' test.txt

打印文件的第3行第2列
sed -n '3,1p' test.txt | awk '{print $2}'

打印文件的总行数
awk 'END{print NR}' test.txt

删除空行(或者说打印非空行的内容)
awk 'NF' test.txt
awk '!/^$/' test.txt
awk '/^[^$]/ {print $0}' test.txt

以:为分隔,打印文件的第一,第二列
awk -F':' '{print $1,$2}' test.txt

基本上比较常用,不过这是属于基础的用法。最常用的还是-F加上分隔符,不加是按照空格来分隔



实际与特殊用法



统计以root开头的
awk -F: '/^root/' /etc/passwd

统计以root开头的,并列出第一列和第7列
awk -F: '/^root/{print $1,$7}' /etc/passwd

统计有几个具有超级用户权限的用户
awk -F: '$3==0 {print $1}' /etc/passwd


检测系统中密码为空的用户
(正确的看这个,目前发现网上大部分文章的这个内容有错误,难到是他们的系统版本老)
(在centos系统中,密码如果为空,会用两个!!表示,length统计以:为分隔的$2其长度为2,而正常的用户且密码不为空的,是采用加密存储的,其长度远大于2)
awk -F: 'length($2)==2 {print $1}' /etc/shadow

这个是统计以:为分隔的$2等于两个叹号的用户,来确认密码为空的用户
awk -F: '$2=="!!" {print $1}' /etc/shadow



统计第一列和第7列,并以tab键空格隔开
awk -F ':' '{print $1"\t"$7}'  /etc/passwd

统计Linux系统中每个用户所用的shell
cat /etc/passwd | awk -F ":" '{print $1" : "$7}'


统计linux系统中所有的用户数
cat /etc/passwd | awk '{count++}END{print count}'
cat /etc/passwd |wc -l


统计某个文件夹下文件所占的字节数(这个是列出字段后用了一个求和运算;先执行BEGING,再执行END操作)
ls -l | awk 'BEGIN{size=0}{size=size+$5}END{print size}'
统计某个文件夹下文件所占的字节数,按M显示(这个是列出字段后用了求和与除法运算;先执行BEGING,再执行END操作)
ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print size/1024/1024,"M"}'
统计目录或目录下文件大小常用的实际是du,并且ls -l只能看到非隐藏的文件,上方的示例只是告诉你上面可以用awk实现运算,并是不常用用法,属于特殊用法
du -sh /home/
du -sh /home/*


netstat统计TCP连接数
netstat -an | awk '/^tcp/{++S[$NF]}END{for(a in S) print a, S[a]}'
这个是常见的用于排查网络异常的命令,但是一般来说,如果你调优的参数没问题,也没有恶意爬取,正常的并发量大,只能是扩充节点了(注意一下这个参数net.ipv4.tcp_tw_recycle,虽然快速回收了time_wait,但是nat的网络环境下开启会导致异常)


统计登录失败的ip和次数( $(NF-3) 是倒数第4个字段,-1是倒数第2个字段)
awk '/Failed/{ip[$(NF-3)]++}END{for(i in ip){print i,ip[i]}}' /var/log/secure 
or
awk '/Failed/{ip[$(NF-3)]++}END{for(i in ip){print ip[i],i}}' /var/log/secure
awk '/Failed/{print $(NF-3)}' /var/log/secure |sort -n |uniq -c|sort -n


以空格为分隔,将文件内容变成一行一行的格式
awk '{for(i=1;i<=NF;++i) print $i}' test.txt 
这是主要用于文本,转换查看的格式,使内容更明了


grep



是一种强大的文本搜索工具,可使用正则表达式搜索文本,并把匹配的行打印出来
用法简单,常用的参数和正则也就几个,就不分成多个部分了



-A<显示行数>:  除了显示符合范本样式的那一列之外,并显示该行之后的内容
-B<显示行数>:  除了显示符合样式的那一行之外,并显示该行之前的内容
-C<显示行数>:  除了显示符合样式的那一行之外,并显示该行之前后的内容
--include:     指定搜索包含的文件格式,用法 --include=test/.txt或--include={txt,.csv}
--exclude:     排除的文件,用法 --exclude=test/.txt或--exclude={txt,.csv}
--exclude-dir  排除指定目录 --exclude-dir=.svn 
-c:            统计匹配的行数
-e:            实现多个选项间的逻辑or 关系
-E:            扩展的正则表达式
-f FILE:       从FILE获取PATTERN匹配
-F:            相当于fgrep
-i:            忽略字符大小写的差别。
-n:            显示匹配的行号
-o:            仅显示匹配到的字符串
-q:            静默模式,不输出任何信息
-r:            递归搜索目录
-s:            不显示错误信息。
-v:            显示不被pattern 匹配到的行,相当于[^] 反向匹配
-w :           匹配整个单词


常见用法



单个文件查询
grep "log4j" test.txt 

多个文件查询
grep "select" *.txt

只查看字段在哪个文件
grep -L "select" *.txt

递归的从当前目录,查询字段(忽略大小写并显示行号)
grep -irn "log4j" ./ 

输出可匹配字符的行的总数
grep -c "34.145.117.149" test.txt

显示匹配字符的行和行号
grep -n "34.145.117.149" test.txt 

显示开头不是注释的行和行号
grep -vn "^#" test.txt

匹配非注释行和非空白行
cat test.txt | grep -v ^# |grep -v ^$
grep -Ev '^$|#' test.txt
egrep -v "^$|^#" test.txt

匹配开头不是select的行(或者说不匹配开头是select的行,^ 在字符类符号,括号[]之内与之外是不同的,在 [] 内代表反向选择,在 [] 之外则代表定位在行首)
grep '^[^select]' *.txt 
grep -v '^select'  *.txt

匹配结尾不是log的行
grep  -v  log$  *.txt



以下用法出现的次数相比上面较少

过滤包含字符filed的行以及下两行
grep -A2 filed test.txt

过滤包含字符filed的行以及前2行
grep -B2 filed test.txt

过滤包含filed的行以及其上下各1行
grep -C1 filed test.txt

严格匹配字符,要求行内的字符必须和匹配的字符完全一致
grep -w filed test.txt
如果文件中的字符不是filed,而是filed123  Filed file 都不可以

过滤含有数字的行(也可过滤指定集合,[a-z],[A-Z],[0-9] [a-zA-Z0-9])
grep -n '[0-9]' test.txt

匹配开头不是数字的行(空行不会被匹配)
grep -n '[^0-9]' test.txt

排除开头是数字的行(和上面的区别是,除了开头是数字的,其他的都要,空行也会被匹配)
grep -vn '^[0-9]' test.txt

找包含字符串log4j的文件,不包括proc、boot、sys目录
grep -r --exclude-dir={proc,boot,sys} log4j /

找包含字符串log4j的文件,不包含png,jpg结尾的文件
grep -r --exclude=*.{png,jpg} log4j ./
03-23 19:41