shell三剑客grepsedawk精讲

Posted 如何在5年薪百万

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shell三剑客grepsedawk精讲相关的知识,希望对你有一定的参考价值。

总览

  • grep 文本过滤器
  • sed 流编辑器
  • awk 报告生成器

grep

基本语法

  1. 以行为单位过滤
  2. 通过匹配规则,对每一行进行匹配查找进行filter操作,匹配上的输出改行,否则丢弃改行
## 方式1
grep [option][pattern][file1,file2,file3]

## 方式2(常用)
comand | grep pattern
选项含义
-v取反,不包含某一行信息(常用)
-i忽略大小写
-n显示行号
-r递归搜索
-E支持正则匹配
-F不使用正则,按照字面匹配(刚好搜索内容包含特殊字符)

输出文件case.sh中不包含if的行

[root@server1 shell]# grep -v if case.sh 
#!/bin/bash

case $1 in
	jack )
	echo "hello jack"
	;;
	mike )
	echo "hello mike"
	;;
	* )
	echo "every one"
	;;
esac	

将文件中包含echo或者mike的行输出

竖杠是正则表达式,如果不使用-E,会当做一整个字符串匹配

[root@server1 shell]# grep -E "echo | mike" case.sh 
	echo "hello jack"
	echo "hello mike"
	echo "every one"

递归某个目录下所有文件,没有目录是默认当前目录及子目录

[root@server1 dir]# grep -r case ../
../dir/test.sh:case1
../case.sh:case $1 in

tip1 输出结果前后N行

生产查询log时,单纯查询error关键字看不到内容,通过下面方法可以输出前后n行,获取鞥多信息

件控制:

  • B, --before-context=NUM 打印以文本起始的NUM 行
  • A, --after-context=NUM 打印以文本结尾的NUM 行
[root@server1 log]# grep -A5 -B3 php yum.log-20200104
Oct 13 14:39:24 Installed: mailcap-2.1.41-2.el7.noarch
Oct 13 14:39:25 Installed: httpd-2.4.6-90.el7.centos.x86_64
Oct 13 14:39:31 Installed: libzip-0.10.1-8.el7.x86_64
Oct 13 14:39:31 Installed: php-common-5.4.16-46.el7.x86_64
Oct 13 14:39:32 Installed: php-cli-5.4.16-46.el7.x86_64
Oct 13 14:39:32 Installed: php-5.4.16-46.el7.x86_64
Nov 19 11:46:54 Updated: 7:device-mapper-1.02.158-2.el7_7.2.x86_64
Nov 19 11:46:54 Updated: 7:device-mapper-libs-1.02.158-2.el7_7.2.x86_64
Nov 19 11:46:54 Installed: 7:device-mapper-event-libs-1.02.158-2.el7_7.2.x86_64
Nov 19 11:46:54 Installed: libaio-0.3.109-13.el7.x86_64
Nov 19 11:46:54 Installed: device-mapper-persistent-data-0.8.5-1.el7.x86_64

sed

stream editor 流编辑器,对标准输出或者文件逐行进行处理工具。

总结:sed本质上是针对文本以行为单位进行增删改查的工具。

语法格式

# 格式1
stdout | sed [option] "pattern command"
# 格式2
sed [option] "patthern command" file
 

逐行匹配,匹配上的行执行命令。匹配不上的行不做处理直接跳过。

和grep处理过程类似,只是grep类似filter过滤器,而sed类似于map转换器

option讲解

选项含义
-n只打印匹配航
-e直接在命令行进行sed编辑 ,default
-i直接修改源文件
-r支持正则标傲世
-f从指定文件读取编辑动作执行

-n 静默输出

# -n静默模式,只打印匹配到的行
[root@server1 shell]# sed '/python/p' sed_01.sh
#!/bin/bash

I love python
I love python
I love Python
注:sed默认打印原始行,通过-n可以只打印匹配到的行

/正则表达式/

[root@server1 shell]# sed -n '/python/p' sed_01.sh
I love python

-e 从编辑流获取执行

  1. 多个编辑命令需要-e串起来
[root@server1 shell]# sed -n -e '/python/p' -e '/Python/p' sed_01.sh
I love python
I love Python
  1. 可以读取管道符的结果,而不需要从文件读取

-f 读取文件执行

# 把命令放入文件
[root@server1 shell]# cat edit.sed
/python/p
# 执行时从文件读取执行命令
[root@server1 shell]# sed -n -f edit.sed sed_01.sh
I love python

-r 扩展正则表达式

没有-r则不支持正则表达式写法

[root@server1 shell]# sed -n -r '/python|Python/p' sed_01.sh
I love python
I love Python

-i 直接修改源文件

# 替换命令直接对源文件生效
[root@server1 shell]# sed -i 's/love/like/g' sed_01.sh
[root@server1 shell]# cat sed_01.sh
#!/bin/bash

I like python
I like Python

pattern详解

pattern即匹配规则,sed对于匹配上的行才会处理

匹配模式含义示例
10command匹配第10行sed -n “17p” file 打印17行
10,20command10~20行sed -n “17,20p” file 打印17到20行
10,+5command10行~10+5行
/pattern1/command匹配到pattern1的行sed -n “/^root/p” file 打印以root开头德夯
/pattern1/,/pattern2/command从匹配到pattern1开始到匹配到pattern2的行结束
10,/pattern1/command从10行开始到匹配到pattern1结束

只有上述匹配模式,都好代表范围

示例

# 打印17行
[root@server1 shell]# sed -n '17p' /etc/passwd
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
# 打印17到20行
[root@server1 shell]# sed -n '17,20p' /etc/passwd
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin

# 打印以nginx开头的行
[root@server1 shell]# sed -n -r '/^nginx/p' /etc/passwd
nginx:x:997:993:Nginx web server:/var/lib/nginx:/sbin/nologin

# 以mail开头的行到以ftp开头的行
[root@server1 shell]# sed -n -r '/^mail/,/^ftp/p' /etc/passwd
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

编辑命令详解

命令含义
p打印
aappend 行后追加
iinsert 行前追加
rread 外部文件读入,行后追加
wwrite 匹配航写入外部文件
d删除
s/old/new替换第一个
s/old/new/g全部替换
s/old/new/ig全局替换忽略大小写

d 删除行

# 删除1到10行
sed -i '1,10d;p' passwd

# 删除所有不能登录用户
[root@server1 shell]# sed -i '/\\/sbin\\/nologin/d' passwd
[root@server1 shell]# cat passwd
root:x:0:0:root:/root:/bin/bash
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt

追加行

-a 在匹配到的行后追加一行

[root@server1 shell]# sed -i '/\\/bin\\/bash/a  this is user which can login' passwd
[root@server1 shell]# cat passwd
root:x:0:0:root:/root:/bin/bash
this is user which can login
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt

-i 在匹配到的行前插入(append插入行后)

[root@server1 shell]# sed -i '/^mail/,/^ftp/i ----insert before line matched----' passwd
[root@server1 shell]# cat passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
----insert before line matched----
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
----insert before line matched----
operator:x:11:0:operator:/root:/sbin/nologin
----insert before line matched----
games:x:12:100:games:/usr/games:/sbin/nologin
----insert before line matched----
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin

-r 读取文件中的内容,append匹配到的行后

[root@server1 shell]# sed -i '/root/r list' passwd
[root@server1 shell]# cat passwd
root:x:0:0:root:/root:/bin/bash
first line ddddd
2nd line xxxxx
3rd line ------
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
first line ddddd
2nd line xxxxx
3rd line ------

-w 匹配到的行输出到文件

sed '/root/w output.txt' passwd

[root@server1 shell]# clear
[root@server1 shell]# cat output.txt
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

修改

替换所有的root为XXXX

[root@server1 shell]# sed -i 's/root/XXXX/g' passwd
[root@server1 shell]# cat passwd
XXXX:x:0:0:XXXX:/XXXX:/bin/bash

反向引用

引用匹配到的内容作为操作对象

# 给匹配到的内容加s
[root@server1 shell]# cat sed_02

HadAAP is good
HadBBc is not good
HadCCe  is really shit
[root@server1 shell]# sed -i 's/Had.../&s/g' sed_02
[root@server1 shell]# cat sed_02

HadAAPs is good
HadBBcs is not good
HadCCes  is really shit

可以使用\\1做部分引用替换,用到时在查询

查询精讲

使用shell脚本统计配置文件

#!/bin/bash

FILE_NAME=$1



function get_all_sections

        echo ` sed  -n  '/\\[.*\\]/p' $FILE_NAME | sed -e 's/\\[//g'  -e 's/\\]//g'`



function count_section

        index=0
        for i in `get_all_sections`
        do
             index=`expr $index + 1`
             num=`sed -n '/\\['$i'\\]/,/\\[.*\\]/p' $FILE_NAME | grep -v '^#' | grep -v '^$' | grep -v '\\[.*\\]' | wc -l`
             echo  "$index : $i $num"
        done


count_section

统计结果

[root@server1 shell]# sh parse_mycnf.sh my.cnf
1 : mysqld 7
2 : mysql-03 3
3 : mysql-01 8
4 : mysql-02 6
5 : sdf 0

如何排除掉空行

#排除掉结果中的空行

命令| grep -v '^$' 

删除精讲

删除配置文件中所有空行和以#开头的注释行

sed -i '/[:blank:]*#/d;/^$/d' nginx.conf

分号可以隔开多个sed命令

给非注释行添加*

  1. 非#开头的行匹配规则
  2. 反向引用,如何添加字符
sed -i 's/^[^#]/\\*&/g' nginx.conf

修改精讲

  1. 掌握模式匹配控制修改的范围
  2. 全局修改与局部修改
# 1到10行中匹配并替换
1,10s/old/new
 
# 替换第一行中的root为Root
sed -i '1s/root/Root/g' passwd

# 替换5到10行中的的 /sbin/nologin为/bin/bash

sed -i '5,10s/\\/sbin\\/nologin/\\/bin\\/bash/g' passwd

# 匹配到 /sbin/nologin的行,并替换其中的login为LOGIN

sed -i '/\\/sbin\\/nologin/s/login/LOGIN/g' passwd

# 删除文本中所有的数字

sed -i 's/[0-9]*//g' nginx.conf

s前可以写sed的匹配规则,s后写替换规则

追加精讲

  • a append匹配后追加
  • i insert行前插入
  • r 读取文件内容追加到侯敏
  • w 匹配行写入文件
# 在第十行后面追加linebeadhn
sed -i '10a add linebeadhn' passwd
#  10行到20行前insert字符
sed -i '4,10i insert beform line' passwd

# 将/etc/fstab文件内容添加到20行后
sed -i '20r /etc/fstab' passwd

# 匹配到/bin/bash的行并追加文件内容
 sed -i '/\\/bin\\/bash/r /etc/fstab' passwd
 
# 匹配包含内容的行并输出到  out.bash文件中

sed -i '/\\/sbin\\/nologin/w out.bash' passwd

AWK命令详解

awk理解为报告生成器,有专门讲awk编程的教材。

个人认为把一个命令做成编程的复杂度度,是不太合适的

语法格式

## 格式1
awk 'BEGINpatterncommandsEND' filename

## 格式2
管道 | awk命令
  • BEGIN 前置命令,正式处理数据之前执行
  • pattern 匹配模式
  • commands 处理命令,可能多行
  • END 处理完所有匹配数据后执行

学习完awk后对这个命令的设计感到震撼,有些人终其一生也无法写出类似的工具,像伟大程序员执行

awk命令本质上格式如下
awk '' filename

  • 代表要执行的命令,多个可以串行的
  • BEING是修饰,代表后面的命令需要在文件打开前执行
  • END修饰的,代表里面的命令在文件关闭后执行
  • 没有修饰符的代表,读取每一行是执行
  • 之外的代表过滤器,符合匹配规则的行才会被处理

这个很接近于响应式编程,每一行文件数据像一个个小球被抛出,被awk逐个过滤和处理

awk内置变量

内置变量含义
$0整行内容
$n分隔符后,代表第n个变量
NFNumber of Field当前行字段个数,也就是多少列
NRNum of row 当前的行号,从1开始计数
FNR多文件处理时,每个文件行号单独计数,都是从0开始
FSfile separator,默认空格或者tab
RSrow separator 输入行分隔符。默认回车换行
OFS输出字段分隔符。默认空格
ORS输出行分隔符,默认为回车换行
FILENAME文件名
ARGC命令行参数个数
ARGV命令行参数数组
# 打印passwd的第7列,分隔符为冒号
awk 'BEGINFS=":"print $7' passwd

# 默认分隔符获取年龄
[root@server1 shell]# cat awktest
zhangsan 12 shanghai
lisi     33  beijing
wangwu   33  chengdu
[root@server1 shell]# awk 'print $2' awktest
12
33
33

# NF、NR、FNR(FNR对多个文件能看到效果)

1
2
3
[root@server1 shell]# awk 'print FNR' awktest
1
2
3
[root@server1 shell]# awk 'print FNR' awktest passwd

# 指定row的分隔符为---而不是回车

[root@server1 shell]# awk  'BEGINRS="---"print $0' awktest
zhangsan 12 shanghai
lisi     33
 beijing  leaderwangwu   33  chengdu

# 输出文件名和参数个数
[root@server1 shell]# awk 'print FILENAME,ARGC' awktest
awktest 2

输出文件分隔符

当awk输出时,默认的分隔符

awk printf 格式化输出命令

  • $s string字符串
  • $d decimal 十进制
  • $f float 浮点型
  • $x hexa deciaml 十六进制
  • $o 八进制
  • $e 科学计数法
  • $c 打印单个字符的ASCII码
  • - 左对齐
    • 右对齐
# 使用printf 输出,下面命令等同于print默认效果
awk 'BEGINFS=":"printf "%s\\n",$1' passwd
# 使用printf格式化输出
1. 左对齐
2. 每个变量固定20位长度
[root@server1 shell]# awk 'BEGINFS=":"printf "%20-s%20-s\\n",$1,$7' passwd
root                /bin/bash
bin                 /sbin/nologin
daemon              /sbin/nologin
adm                 /sbin/nologin
lp                  /sbin/nologin

# 
# 第三位16进制输出,第四位十进制输出
[root@server1 shell]# awk 'BEGINFS=":"printf "hex: %x  decimal: %d\\n",$3,$4' passwd
hex: c0  decimal: 192
hex: 51  decimal: 81
hex: 3e7  decimal: 997

模式匹配

  • RegExp正则匹配
  • 关系运算符

正则匹配

root❌0:0:root:/root:/bin/bash
operator❌11:0:operator:/root:/sbin/nologin

运算符匹配

  • != 不等于
  • == 等于
  • ~ 匹配正则
  • !~不匹配正则
# 输出第三行小于50的行
[root@server1 shell]# awk 'BEGINFS=":"$3<50print $0' passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologi

# 第七个字段为/bin/bash的符号

[root@server1 shell]# awk 'BEGINFS=":"$7="/bin/bash"print $0' passwd
root x 0 0 root /root /bin/bash
bin x 1 1 bin /bin /bin/bash
daemon x 2 2 daemon /sbin /bin/bash
adm x 3 4 adm /var/adm /bin/bash
lp x 4 7 lp /var/spool/lpd /bin/bash
sync x 5 0 sync /sbin /bin/bash
shutdown x 6 0 shutdown /sbin /bin/bash

匹配正则表达式(强大)


打印第三个字段为3个数字的行,~代表匹配正则表达
[root@server1 shell]# awk  'BEGINFS=":"$3~/[0-9]3,/print $0' passwd
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
polkitd:x:999:997:User for polkitd:/:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
nginx:x:997:993:Nginx web server:/var/lib/nginx:/sbin/nologin
uwsgi:x:996:992:uWSGI daemon user:/run/uwsgi:/sbin/nologin

多个运算符

打印第一列为mail或者ftp的行

[root@server1 shell]# awk  'BEGINFS=":"$1=="ftp" || $1=="mail"print $0' passwd
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

非常强大,难点在于同一个功能多种实现,需要掌握一套方法,而不是所有方法。

表达式用法

普通的加减乘除都可以用,关注其效果即可

+
-
*
/
%
++x
x--
# 自定义变量控制和输出
[root@server1 shell]# awk 'BEGINvar=20;var2=30;var1="hello" ;print var,var1,var*var2'
20 hello 600

# 统计空白行
1. 匹配空白行
2. 每次匹配时执行sum+=
3. 执行完毕后执行END中命令
[root@server1 shell]# awk '/^$/sum;sum++ENDprint sum' /etc/services
17

求平均分

[root@server1 shell]# cat awktest
zhangsan  83 83 83 98
lisi      93 93 91 09
wangwu    93  93 02 02
[root@server1 shell]# awk 'BEGINprint "姓名","平均分"avg=($2+$3+$5)/3print $1,avg ' awktest
姓名 平均分
zhangsan 88
lisi 65
wangwu 62.6667

条件语句

语法格式

if(condition)
    action1
else if
    action2
else
    action3
输出第三行大于50的行
[root@server1 shell]# awk 'if ($3 > 50 ) print $0' awktest 
zhangsan  83 83 83 98
lisi      93 93 91 09
wangwu    93  93 02 02

循环语句

## do 循环
do while
do
    actions
while(conition)

## for 循环
同java

计算 0到99的和,这里使用文件的方式执行awk命令

[root@server1 shell]# cat forsum.awk 
BEGIN
	for( i=0;i<100;i++)
		sum = sum+i
	
	print sum



[root@server1 shell]# awk -f forsum.awk 
4950

计算平均值和每列的和

这里注意理解awk的变量传递。变量可以全局使用

[root@server1 shell]# awk -f forsum2.awk awktest 
name      chinaese  math      eng       avg       
zhangsan  83        83        83        83.00                                                                                               
lisi      93        93        91        92.33                                                                                               
wangwu    93        93        2         62.67                                                                                               
sum       269       269       176       
[root@server1 shell]# cat forsum2.awk 
BEGIN
	printf "%-10s%-10s%-10s%-10s%-10s\\n","name","chinaese","math","eng","avg"



	total=$2+$3+$4
	avg=total/(NF-2)
	printf "%-10s%-10d%-10d%-10d%-100.2f\\n",$1,$2,$3,$4,avg
        sum1+=$2
        sum2+=$3
        sum3+=$4

END
	 printf "%-10s%-10d%-10d%-10d\\n","sum",sum1,sum2,sum3



字符串函数

函数名解释
length(str)返回长度
index(str1,str2)在str1中查找str2的位置,坐标1开始
tolower(str),toupper(str)大小写
substr(str,m,n)从str的m开始,截取n位
split(str,arr,fs)按照fs切割字符串,结果保存在arr中
match9(str,RE)在str中正则匹配,返回位置
sub(str,RE,new)替换字符,gsub全局替换

查询ea在字符串中位置

awk 'BEGINprint index("i have a dream","ea")'

用到时查询记得不一一介绍

常用选项

  • -v 参数传递,引入外部变量
  • -f 指定脚本文件
  • -F 指定分隔符
  • -V 查看版本号
# 使用外部参数是,需要先用-v引入
[root@server1 shell]# awk -v var=$var 'BEGINprint var'
hello
[root@server1 shell]# awk  'BEGINprint var'


数组用法

shell中数组用法

# 定义数组
[root@server1 shell]# array=("a" "b" "c")
# 获取数组长度
[root@server1 shell]# echo $#array[@]
3
# 获取单个元素
[root@server1 shell]# echo $array[1]
b

# 删除元素
[root@server1 shell]# unset array[2]
# 打印全部元素
[root@server1 shell]# echo $array[@]
a b

# 分片元素,访问下标1到3的元素
echo $array[@]:1:3

# 元素替换,所有小写e替换为大写E
$array[@]/e/E

# 数组遍历,同集合遍历

for i in array
do
    echo $i
done

awk中数组用法

使用split分割字符串保存在数组中

[root@server1 shell]# awk 'BEGINstr="a b c d e";split(str,array," ");for(i in array) print array[i]' 
d
e
a
b
c

awk中除了数字还能使用字符作为数组下标

观察下面例子,通过字符串作为数组下标可以更好的处理字符统计

[root@server1 shell]# awk -f sumtest1.awk awktest1 
username  success             fail                total               
zhangsan  876090              6969                883059              
wangwu    2436360             276396              2712756             
lisi      3110107             2993939390          2997049497          
[root@server1 shell]# cat sumtest1.awk 
BEGIN
	printf "%-10s%-20s%-20s%-20s\\n","username","success","fail","total"


	SUCCESS[$2]+=$5
	FAIL[$2]+=$7
        TOTAL[$2]=SUCCESS[$2]+FAIL[$2]
 


END
	for( i in SUCCESS)
	printf "%-10s%-20d%-20d%-20d\\n",i,SUCCESS[i],FAIL[i],TOTAL[i]
		


[root@server1 shell]# head awktest1
user lisi     update success 239239  fail 230303030
user lisi     update success 239239  fail 230303030
user wangwu   insert success 203030  fail 23033
user lisi     update success 239239  fail 230303030
user lisi     update success 239239  fail 230303030
user wangwu   insert success 203030  fail 23033
user lisi     update success 239239  fail 230303030
user lisi     update success 239239  fail 230303030
user wangwu   insert success 203030  fail 23033
user lisi     update success 239239  fail 230303030

以上是关于shell三剑客grepsedawk精讲的主要内容,如果未能解决你的问题,请参考以下文章

Shell脚本----三剑客命令(grepsedawk)

正则表达式及编程三剑客(grepsedawk)命令详解

grepsedawk三剑客

文本三剑客(grepsedawk)

三剑客基础详解(grepsedawk)

linux三剑客(grepsedawk)