shell快讲第四节--sed与awk

shell快讲第一节--shell基础
shell快讲第二节--管道与重定向
shell快讲第三节--正则表达式
shell快讲第四节--sed与awk
shell快讲第五节--shell语法基础
shell快讲第六节--shell函数编程
shell快讲第七节--shell编程规范与调试
shell快讲第八节--shell编程实战

sed与awk

简介

这节课讲sed和awk,这两个可能是在写脚本,尤其是监控脚本最常用到的两个命令了,所以按照传统,我们讲一下三剑客,这几个命令其实都有超级复杂的用法,但是本着学以致用的原则,我们就介绍我们最常接触的

sed

sed属于流媒体编辑器(stream editor),我们可以用它编辑任何我们想编辑的东西(键盘输入,文本,管道,字符串,变量),这里我们只研究简单的场景,下面看下常见的代码,pattern代表正则表达。

>>> echo 'hello word' |sed 's/word/world/'
>>> sed 's/pattern/replace_string' file #将file中的字符1全部替换成字符2
>>> sed '2,5s/pattern/replace_string' file #将file第2行到第5行进行字符替换
>>> cat file | sed sed 's/pattern/replace_string'
>>> sed 's/pattern/replace_string' file >newfile #将修改结果重定向到一个新文件
>>> sed -i 's/pattern/replace_string' file #将修改结果到源文件
>>> sed  4a\newline testfile #使用sed 在第四行后添加新字符串 
>>> sed '2d' testfile #删除第二行
>>> sed '2,5d' testfile #删除第2到第5行
>>> sed '3,$d' testfile #删除第3到最后一行
>>> sed '2a hellworld' #在第2行后加 注意上面的4a\newline的写法
>>> sed '2i hellworld' #插在第二行前变成第二行
>>> sed '2c helloworld' #第二行内容用helloworld取代
>>> sed -n '5,7p' testfile #仅打印5到7行的字符串
>>> sed -n '/aNum/p' testfile #仅打印包含aNum字符串的行
>>> sed  '/aNum/a/helloworld' testfile #将所有包含aNum字符串的行后面加上helloworld作为一行
>>> sed '/helloworld/d' testfile #将包含helloworld的行删除
>>> sed -i 's/\.$/\!/g' test.txt #利用 sed 将 regular_express.txt 内每一行结尾若为 . 则换成 !
>>> sed -i '$a  This is a test'test.txt #利用 sed 直接在 regular_express.txt 最后一行加入 # This is a test

sed替换文件内的字符串是我们最常见的,如上命令仅仅会替换到每行的第一个匹配,如果一行只有一个匹配那自然没问题,如果一行存在多个匹配的时候呢,我们就要使用如下的方法:

>>> echo 'hello word word word word' |sed 's/word/world/g' #匹配所有的word
>>> echo 'hello word word word word' |sed 's/word/world/2g' #匹配前1
>>> echo 'hello word word word word' |sed 's/word/world/Ng' #匹配N-1个
>>> sed -e 's/brown/green/; s/dog/cat/' data1.txt #复合用法

sed 脚本

sed本身是支持自己的语法的,解释器即是sed,本文只写个简单的例子,另外sed就抠到这里,sed的某些用法也许一辈子也用不到

>>> cat script1.sed
    s/brown/green/
    s/fox/elephant/
    s/dog/cat/
>>> sed -f script1.sed testfile #将sed脚本作用于某个文件
>>> ./script1.sed testfile

sed常见选项

选项太多,大家下去自行使用体会

a\ 在当前行下面插入文本。
i\ 在当前行上面插入文本。
c\ 把选定的行改为新的文本。
d 删除,删除选择的行。
D 删除模板块的第一行。
e 按顺序匹配并操作
s 替换指定字符
h 拷贝模板块的内容到内存中的缓冲区。
H 追加模板块的内容到内存中的缓冲区。
g 获得内存缓冲区的内容,并替代当前模板块中的文本。
G 获得内存缓冲区的内容,并追加到当前模板块文本的后面。
l 列表不能打印字符的清单。
n 读取下一个输入行,用下一个命令处理新的行而不是用第一个命令。
N 追加下一个输入行到模板块后面并在二者间嵌入一个新行,改变当前行号码。
p 打印模板块的行。
P(大写) 打印模板块的第一行。
q 退出Sed。
b lable 分支到脚本中带有标记的地方,如果分支不存在则分支到脚本的末尾。
r file 从file中读行。
t label if分支,从最后一行开始,条件一旦满足或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。
T label 错误分支,从最后一行开始,一旦发生错误或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。
w file 写并追加模板块到file末尾。  
W file 写并追加模板块的第一行到file末尾。  
! 表示后面的命令对所有没有被选定的行发生作用。  
= 打印当前行号码。  
# 把注释扩展到下一个换行符以前

awk

awk也是我讨厌的命令之一,它很强大,但是复杂的用法着实让人记不住,当然它是个优秀的软件,所谓三大命令之首,awk脚本的结果基本如下:

**awk 'BEGIN{print "start"} pattern {commands} END{print "end"}' **

工作原理

shell快讲第四节--sed与awk_第1张图片
1-05-01.png

1.先执行BEGIN{commands}语句块。这个语句块是可选的

2.从文件或者管道或者其他等等方式中读取一行,然后执行pattern,重复这个过程,直到读取完毕

3.执行END{commands}语句块。这个语句块是可选的

4.print 参数是 以逗号作为分隔符,参数打印时是以空格作为定界符

看如下例子:

>>>echo -e "line1\nline2" |awk 'BEGIN{print "start"} {print} END{print "END"}'
>>>echo -e "hello world" |awk 'BEGIN{print "start"} {print $1} END{print "END"}'
>>>echo -e "hello world" |awk '{print $1}' #当然这是我们最常用的,省略了begin和end

特殊变量

awk有一些特殊变量,就像上面的$1,这些变量集中整理一下,当然这个使用起来是比较简单的,只要知道NR,NF这两个特殊的就行了,后面的命令演示都会用到

特殊变量 描述
NR 表示记录数量,在执行过程中对应于当前行号
NF 表示字段数量,在执行过程中对应当前行的字段数
$0 这个变量包含执行过程当前行的内容
$1 这个变量包当前行的第一个字段
FS 输入隔断符号
OFS 输出分隔符
RS 输入行分隔符
ORS 输出行分隔符

awk选项

选项 选项
-F fs 指定输入文件折分隔符 -v var=value 赋值一个用户定义变量。
-f scripfile 从脚本文件中读取awk命令

例子演示

>>> ps axu |awk '{print $1,$4}'
>>>cat /etc/passwd |awk '{print $1,$3}' 
>>>cat /etc/passwd |awk -F: '{print $1,$3}' #看下和如上命令结果有何不同
>>>awk -F: '{printf "%-8s %-8d\n",$1,$3}' /etc/passwd #格式化输出,大家尽量理解下格式化的意思
------带上BEGIN---------
>>> awk 'BEGIN{FS=":"}{print $1,$3}' /etc/passwd #感受下BEGIN的作用
>>> awk 'BEGIN{FS="[:/]"}{print $NF}' /etc/passwd #先用:做分割,再用/做分割,感受下不同,也记住这诡异的语法
>>> awk -F: 'BEGIN{print "用户    ID"}$3>=1000{print $1,$3}' /etc/passwd #打印用户id>=1000的用户,这里体验一下运算符号(==,>,<,>=,<=)
>>> awk -F: 'BEGIN{OFS="|";print "用户    ID"}$3>=1000{print $1,$3}' /etc/passwd #输出内容自定义分隔符
>>> awk -F: '$1=="root" || $1=="capricorn" {print $1,$3}' /etc/passwd #打印用户root和capricorn的ID
>>> awk  -F: '/^ssh/ {print $0}' /etc/passwd #打印ssh开头的行,这里体验一下针对整行的正则表达式
>>> awk -F: '$NF ~ /nologin/ {print $1,$3,$NF}' /etc/passwd #输出最后一个字段包含nologin的用户名和ID,感受下如果针对某一个字段进行正则
>>> awk  'BEGIN{FS=":";print "特殊用户"}$1 ~/root/ {print $NF}' /etc/passwd
>>> ls -l *.md|awk '{sum+=$5} END{print sum}' #查找所有md文件并计算总大小
>>> echo "hello world " |awk -v  hostname=$HOSTNAME  '{print $1,hostname}'#从外界传递变量到awk

awk输出个格式化

输出格式化对程序来说不是很必要,格式化主要是为了给人看的,下面大家感受下格式化就行了,这部分不打算展开,大家感受下如下代码,注意这里不是print,是printf,另外注意理解格式化语句,“\n”代表换行,我开始就吃着亏了,一定记得加上。

awk -F: 'BEGIN{printf "%-16s %8s\n","用户","ID"}{printf "%-16s %-8s\n",$1,$3}' /etc/passwd

awk脚本

awk脚本编程老实说是比较复杂的,因为它有自己的语法,这显然增加了学习压力,其实我不太建议上来就研究这么深,如果有需要配合百度琢磨琢磨也就差不多了。

假设有这么一个文件(学生成绩表),懒得复制也可以直接下载score.txt

$ cat score.txt
Marry   2143 78 84 77
Jack    2321 66 78 45
Tom     2122 48 77 71
Mike    2537 87 97 95
Bob     2415 40 57 62

我们的awk脚本如下,这段代码包含了awk脚本的写法,脚本的格式化,运算符,是一个不错的例子,当然是我网上抄的,代码尽量自己敲,下载代码文件

$ cat cal.awk
#!/usr/bin/awk -f
#运行前
BEGIN {
    math = 0
    english = 0
    computer = 0
 
    printf "NAME    NO.   MATH  ENGLISH  COMPUTER   TOTAL\n"
    printf "---------------------------------------------\n"
}
#运行中
{
    math+=$3
    english+=$4
    computer+=$5
    printf "%-6s %-6s %4d %8d %8d %8d\n", $1, $2, $3,$4,$5, $3+$4+$5
}
#运行后
END {
    printf "---------------------------------------------\n"
    printf "  TOTAL:%10d %8d %8d \n", math, english, computer
    printf "AVERAGE:%10.2f %8.2f %8.2f\n", math/NR, english/NR, computer/NR
}
>>> ./1-05.01.awk score.txt #执行脚本获取结果

总结 awk还是不少其他的用法,比如带for循环,比如各种各种的运算表达式,当然目前学的太深好处也不明显(笔者也没研究那么深),大家学的时候要记住技巧,awk的常规格式,这些格式的执行过程是咋样的,如果变成脚本来使用,知道这些原理,具体的细节一旦忘记直接百度查查,基本上就可以满足工作需要,另外技术还是熟能生巧的道理,相信各位一定比笔者用的多,自然未来也会比我更熟悉,这里提供一个超详细的awk讲解

你可能感兴趣的:(shell快讲第四节--sed与awk)