shell工具--sed和awk详解

grep

grep是一款强大的文本过滤工具,按照关键字或者正则表达式进行过滤
具体讲解请看博文
这里写链接内容

sed

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<stdio.h>

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<stdio.h>

int main(){
    return 0;
}
[root@MiWiFi-R1CL-srv ~]# cat test.c
#include<stdio.h>

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<stdio.h>

int main(){
    return 0;
}

例三:
/pattern/s/pattern1/pattern2/: 查找符合pattern的行,将该行第一个匹配pattern1的字符串替换为 pattern2
如下:使用sed注释C代码当中的所有的 printf

[root@MiWiFi-R1CL-srv ~]# cat test.c
#include<stdio.h>

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<stdio.h>

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 #打印35行的信息
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

概念
  1. sed以行为单位处理文件,awk与sed相比不仅能以行为单位,还能以列为单位处理文件。
  2. awk缺省的行分隔符是换行,缺省的列分隔符是连续的空格和Tab,但是行分隔符和列分隔符都可以自定义。
  3. awk实际上是一门很复杂的脚本语言,还有像C语言一样的分支和循环结构,但是基本用法和sed类似。
  4. awk处理的文件既可以由标准输入重定向得到,也可以当命令行参数传入,编辑命令可以直接当命令行参数传入,也可以用-f参数指定一个脚本文件。
    编辑命令的格式为:
    /pattern/{actions}
    condition{actions}
  5. 和sed类似,pattern是正则表达式,actions是一系列操作。awk程序一行一行读出待处理文件,如果某一行与pattern匹配,或者满足condition条件,则执行相应的actions,如果一条awk命令只 有actions部分,则actions作用于待处理文件的每一行。
  6. awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。

下面我们看一下awk的具体用法:

print命令

看下面的例子:

[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定制输入输出符

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

BEGIN&END

概念
  1. BEGIN和END,是awk中两个极具特色的表达式,可以用他们做文本处理之前的准备工作,之后的收尾工具,极具实用价值,基本格式如下:
    BEGIN{} /REG/{} END{}
    BEGIN{} condition{} END{}
  2. 任何在BEGIN之后列出的操作(在{}内)将在awk开始扫描输入之前执行,而END之后列出的操作将在扫描完全部的输入之后执行。
    因此,通常使用BEGIN来显示变量和预置(初始化)变量,使用END来输出终结果。

举例如下:
统计文本中,成绩及格的人数:

[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除了上面的基本命令行的使用方式之外,还有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内置变量

上面的例子中,awk BEGIN{}中间有一个 FS ,这个变量叫做awk的内置变量,类似于shell的内置变量。常见的内置变量
有:

内置变量 作用
ARGC 命令行参数个数
ENVIRON 支持队列中系统环境变量的使用
FILENAME awk浏览的文件名
FNR 浏览文件的记录数
FS 设置输入域分隔符,等价于命令行 -F选项
NF 浏览记录的域的个数
NR 已读的记录数
OFS 输出域分隔符
ORS 输出记录分隔符
RS 控制记录分隔符

猜你喜欢

转载自blog.csdn.net/baidu_37964071/article/details/80987389
今日推荐