grep是一款强大的文本过滤工具,按照关键字或者正则表达式进行过滤
具体讲解请看博文
这里写链接内容
sed是一种流编辑器,它是文本处理中非常中的工具,能够完美的配合正则表达式使用。
1. 处理时,把当前处理的行存储在临时缓冲区中,称为模式空间(pattern space)
2. 接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。
3. 接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。
Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。
sed默认按照基准模式匹配
命令格式
sed [options] 'command' file(s)
sed [options] -f scriptfile file(s)
举例使用
例一:
/pattern/p: 打印匹配pattern的行
[root@MiWiFi-R1CL-srv ~]# cat file
aaaaa
bbbbb
ccccc
ddddd
[root@MiWiFi-R1CL-srv ~]# sed '/^a\+$/p' file
aaaaa
aaaaa
bbbbb
ccccc
ddddd
[root@MiWiFi-R1CL-srv ~]# sed -n '/^a\+$/p' file
aaaaa
注意:
1. 使用p命令时,sed是把待处理文件的内容连同处理结果一起输出到标准输出的,因此p命令表示除了把文件内容打印出来之外还额外打印一遍匹配pattern的行
2. 要想只输出处理结果,应加上-n选项,这种用法相当于grep命令
例二:
/pattern/d: 删除匹配pattern的行
如下:删除C代码中的所有的 printf 语句
[root@MiWiFi-R1CL-srv ~]# cat test.c
#include
int main(){
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
return 0;
}
[root@MiWiFi-R1CL-srv ~]# sed '/^.*printf.*;$/d' test.c
#include
int main(){
return 0;
}
[root@MiWiFi-R1CL-srv ~]# cat test.c
#include
int main(){
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
return 0;
}
注意:
1. sed命令不会修改原文件,删除命令只表示某些行不打印输出,而不是从原文件中删去。 2. 如果要影响源文件,需要使用 -i 选项
如下:
[root@MiWiFi-R1CL-srv ~]# sed -i '/^.*printf.*;$/d' test.c
[root@MiWiFi-R1CL-srv ~]# cat test.c
#include
int main(){
return 0;
}
例三:
/pattern/s/pattern1/pattern2/: 查找符合pattern的行,将该行第一个匹配pattern1的字符串替换为 pattern2
如下:使用sed注释C代码当中的所有的 printf
[root@MiWiFi-R1CL-srv ~]# cat test.c
#include
int main(){
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
return 0;
}
[root@MiWiFi-R1CL-srv ~]# sed '/^.*printf(.*);$/s/^/\/\//' test.c
#include
int main(){
// printf("hello world!\n");
// printf("hello world!\n");
// printf("hello world!\n");
// printf("hello world!\n");
// printf("hello world!\n");
// printf("hello world!\n");
return 0;
}
例四:
/pattern/s/pattern1/pattern2/g:查找符合pattern的行,将该行所有匹配pattern1的字符串替换为 pattern2
与例三不加g的区别是什么呢?如下:
[root@MiWiFi-R1CL-srv ~]# cat file
aaaaa
bbbbb
ccccc
ddddd
[root@MiWiFi-R1CL-srv ~]# sed '/^a\+/p' file
aaaaa
aaaaa
bbbbb
ccccc
ddddd
[root@MiWiFi-R1CL-srv ~]# sed '/^a\+/s/a/~a~/' file
~a~aaaa
bbbbb
ccccc
ddddd
[root@MiWiFi-R1CL-srv ~]# sed '/^a\+/s/a/~a~/g' file
~a~~a~~a~~a~~a~
bbbbb
ccccc
ddddd
例五:
已匹配字符串标记&,可以表示匹配pattern1之后的所有字符:
[root@MiWiFi-R1CL-srv ~]# echo "this is a test line"
this is a test line
[root@MiWiFi-R1CL-srv ~]# echo "this is a test line" | sed 's/\w\+/[&]/g'
[this] [is] [a] [test] [line]
例六:
子串匹配标记\1,\2…\n
[root@MiWiFi-R1CL-srv ~]# echo "test digit 789"
test digit 789
[root@MiWiFi-R1CL-srv ~]# echo "test digit 789" | sed 's/\([0-9]\)\([0-9]\)\([0-9]\)/\3\2\1/'
test digit 987
注意:
(..) 用于匹配子串,对于匹配到的第一个子串就标记为\1,依此类推匹配到的第二个结果就是\2,我们通过对标签进行重组,从而完成数据逆置。
例七:
定址
定址用于决定对哪些行进行编辑。地址的形式可以是数字、正则表达式、或二者的结合。如果没有指定地址,sed 将处理输入文件的所有行。
[root@MiWiFi-R1CL-srv ~]# for i in {a..z};do echo $i;done >> file
[root@MiWiFi-R1CL-srv ~]# sed -n '3,5p' file #打印3~5行的信息
c
d
e
[root@MiWiFi-R1CL-srv ~]# sed '3,5d' file #删除第3行到第5行
a
b
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
[root@MiWiFi-R1CL-srv ~]# sed -n '/^f/,/^h/p' file #打印以’f’开头和以’h’开头的行之间的行
f
g
h
[root@MiWiFi-R1CL-srv ~]# sed -n '/^f/,15p' file #打印以’f’开头的行到第15行的内容
f
g
h
i
j
k
l
m
n
o
退出状态
sed和grep不同,不管是否找到指定的模式,它的退出状态都是0。只有当命令存在语法错误时,sed 的退出状态才不为0。
模式空间和保持空间
在上文我们已经了解到模式空间,现在进一步了解模式空间与保持空间。
模式空间:可以想成工程里面的流水线,数据直接在它上面进行处理。
保持空间:可以想象成仓库,我们在进行数据处理的时候,作为数据的暂存区域。
注意:正常情况下,如果不显示使用某些高级命令,保持空间不会使用到。
sed高级命令
命令 | 作用 |
---|---|
g | 将hold space中的内容拷贝到pattern space中,原来pattern space里的内容清除 |
G | 将hold space中的内容append到pattern space\n后 |
h | 将pattern space中的内容拷贝到hold space中,原来的hold space里的内容被清除 |
H | 将pattern space中的内容append到hold space\n后 |
d | 删除pattern中的所有行,并读入下一新行到pattern中 |
D | 删除multiline pattern中的第一行,不读入下一行 |
x | 交换保持空间和模式空间的内容 |
N | 将下一行添加到pattern space中 |
n | 读取下一行到pattern space |
举例说明:
[root@MiWiFi-R1CL-srv ~]# for i in {1..10};do echo $i;done > file
给每行结尾添加一行空行:
[root@MiWiFi-R1CL-srv ~]# sed 'G' file
1
2
3
4
5
6
7
8
9
10
用sed模拟出tac的功能(倒序输出):
[root@MiWiFi-R1CL-srv ~]# sed '1!G;h;;$!d' file
10
9
8
7
6
5
4
3
2
1
追加匹配到文件结尾:
[root@MiWiFi-R1CL-srv ~]# sed -e '/^7\|^9/H' -e '$G' file
1
2
3
4
5
6
7
8
9
10
7
9
-e选项允许在同一行里执行多条命令,如下,行列转化:
[root@localhost ~]# sed -n 'H;${x;s/\n/ /g;p}' file
1 2 3 4 5 6 7 8 9 10
打印奇偶数行:
[root@localhost ~]# sed -n 'n;p' file
2
4
6
8
10
[root@localhost ~]# sed -n 'p;n' file
1
3
5
7
9
求1~100的和:
方式一:
[root@localhost ~]# seq 100 | sed -n 'H;${x;s/\n/+/g;s/^+//;p}'
1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23+24+25+26+27+28+29+30+31+32+33+34+35+36+37+38+39+40+41+42+43+44+45+46+47+48+49+50+51+52+53+54+55+56+57+58+59+60+61+62+63+64+65+66+67+68+69+70+71+72+73+74+75+76+77+78+79+80+81+82+83+84+85+86+87+88+89+90+91+92+93+94+95+96+97+98+99+100
[root@localhost ~]# seq 100 | sed -n 'H;${x;s/\n/+/g;s/^+//;p}' | bc
5050
方式二:
[root@localhost ~]# seq 100 | sed ':a;N;s/\n/+/g;{$!ba}'
1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23+24+25+26+27+28+29+30+31+32+33+34+35+36+37+38+39+40+41+42+43+44+45+46+47+48+49+50+51+52+53+54+55+56+57+58+59+60+61+62+63+64+65+66+67+68+69+70+71+72+73+74+75+76+77+78+79+80+81+82+83+84+85+86+87+88+89+90+91+92+93+94+95+96+97+98+99+100
[root@localhost ~]# seq 100 | sed ':a;N;s/\n/+/g;{$!ba}' | bc
5050
:a表示标签a,ba表示跳转到a标签,表示最后一行,!表示不做后续操作,所以 ,!ba表示后一行不用跳转到a标签, 结束此次操作。
下面我们看一下awk的具体用法:
看下面的例子:
[root@localhost ~]# cat file
producta 100
productb 200
productc 20
productd 24
producte 234
[root@localhost ~]# awk '/^producta.*$/{print $0}' file
producta 100
使用print命令输出符合正则表达式的结果行,这里的 $0 代表整条记录(行)。
那如何打印具体到列呢?
[root@localhost ~]# awk '/^producta.*$/{print $1}' file
producta
[root@localhost ~]# awk '/^producta.*$/{print $2}' file
100
此时, $1,$2 表示指定的域(列),上面例子可以看出来,空格是其域分割符。
输出多个域时, print 使用 , 作为分隔符,如下:
[root@localhost ~]# awk '/^producta.*$/{print $2,$1,$0}' file
100 producta producta 100
看下面例子:
输出第二列大于50的行:
[root@localhost ~]# awk '$2 > 50{print $0}' file
producta 100
productb 200
producte 234
[root@localhost ~]# awk '$2 > 50{print $1,"yes"} $2 <=50{print $1,"no"}' file
producta yes
productb yes
productc no
productd no
producte yes
同样的,awk支持的命令还有 printf :
[root@localhost ~]# awk '$2 > 50{printf("%s->%s\n",$1,"yes");}$2 < 50{printf("%s->%s\n",$1,"no");}' file
producta->yes
productb->yes
productc->no
productd->no
producte->yes
awk默认按照空格作为分隔符,如果想定制分隔符,常用的方法是使用 -F 选项。
举例如下:
输出第一列:
[root@localhost ~]# echo "hello world" | awk '{print $1}'
hello
若分隔符为:,如下:
[root@localhost ~]# echo "hello:world" | awk '{print $1}'
hello:world
需要我们定义分隔符为:,
[root@localhost ~]# echo "hello:world" | awk -F':' '{print $1}'
hello
举例如下:
统计文本中,成绩及格的人数:
[root@localhost ~]# cat file
zhangsan:23
lisi:60
wangwu:90
zhaoliu:67
tianqi:32
[root@localhost ~]# awk -F':' 'BEGIN{count=0;}$2>=60{count++;}END{printf("total is : %d\n",count);}' file
total is : 3
awk除了上面的基本命令行的使用方式之外,还有awk脚本方式,不过基本写法与命令行相同,语法也是基本类C。
我们看一个例子:
通过awk脚本,统计产品的档次,85分以上,优。75~85,良。60~75,中。60分以下,差。
如下:
成绩文件如下:
[root@localhost ~]# cat file
producta:100
productb:103
productc:17
productd:11
producte:34
productf:90
productg:45
producth:34
producti:103
productj:145
productk:56
productl:24
productm:105
productn:67
producto:89
productp:112
productq:100
productr:68
products:57
awk脚本:
#!/usr/bin/awk -f
BEGIN{
FS=":"
excellent=0;
good=0;
middle=0;
bad=0;
}
{
if($2 > 85){
excellent++;
}
else if($2 > 75){
good++;
}
else if($2 > 60){
middle++;
}
else{
bad++;
}
}
END{
printf("%d\n",excellent);
printf("%d\n",good);
printf("%d\n",middle);
printf("%d\n",bad);
}
运行结果:
[root@localhost ~]# ./level.awk file
9
0
2
9
上面的例子中,awk BEGIN{}中间有一个 FS ,这个变量叫做awk的内置变量,类似于shell的内置变量。常见的内置变量
有:
内置变量 | 作用 |
---|---|
ARGC | 命令行参数个数 |
ENVIRON | 支持队列中系统环境变量的使用 |
FILENAME | awk浏览的文件名 |
FNR 浏览文件的记录数 | |
FS | 设置输入域分隔符,等价于命令行 -F选项 |
NF | 浏览记录的域的个数 |
NR | 已读的记录数 |
OFS | 输出域分隔符 |
ORS | 输出记录分隔符 |
RS | 控制记录分隔符 |