简述
vim 与 sed&awk 的区别:
- 交互式与非交互式
- 文件操作模式与行操作模式
sed
sed是一种流编编器,它是文本处理中非常中的工具,能够完美的配合正则表达式便用,功物能不同凡响。处理时,把当前处理的行存储在临时缓冲区中,称为”模式空间”( oattern space),接看用sed命令处理缓冲区中的内容,处理成后,把缓冲区的内容送往屏幕显示。
sed 会根据脚本命令来处理文本文件中的数据,这些命令要么从命令行中输入,要么存储在一个文本文件中,sed的基本工作方式如下:
1 每次仅读取一行内容;(将文件以行为单位读取到内存(模式空间))
2 根据提供的规则命令匹配并修改数据(sed 默认不会直接修改源文件数据,而是会将数据复制到缓冲区中,修改也仅限于缓冲区中的数据;)
3 将执行结果输出。
当一行数据匹配完成后,它会继续读取下一行数据,并重复这个过程,直到将文件中所有数据处理完毕。
语法: sed [选项] [脚本命令] 文件名
第一种形式:stdout | sed [option] “pattern command”
第二种形式:sed [option] “pattern command” file
常见命令参数:
p==print
d:delete
=:打印匹配行的行号
-n 取消默认的完整输出,只要需要的 (会自动输出处理后的内容,而该选项会屏蔽启动输出,需使用 print 命令来完成输出。)
-e 允许多项编辑 (该选项会将其后跟的脚本命令添加到已有的命令中。)
-i 修改文件内容
-r 不需要转义
注意:& 符号在sed命令中代表上次匹配的结果
基本使用示例
$ echo a a a > afile
## 只替换第一次出现的a字符
$ sed 's/a/aa/' afile
# 结果 aa a a
## sed 替换特殊字符时 s/a/aa/ 这种格式可以替换称为 s!a!aa (!为随意字符)
$ sed 's!/!aa/' afile
## sed -e 多个命令的使用
$ sed -e 's/a/aa/' -e 's/aa/bb/' afile
## 等同于上面的效果
$ sed 's/a/aa/;s/aa/bb/' afile
## 执行多个文件
$ sed 's/a/aa/;s/aa/bb/' afile bfile cfile
## 将修改的内容写回到源文件
$ sed -i 's/a/aa/;s/aa/bb/' afile bfile cfile
## 将修改的内容重定向写入到bfile中
$ sed 's/a/aa/;s/aa/bb/' afile > bfile
## 将/home/a.txt文件的前5行中第一个三个字符替换为空
$ head -5 /home/a.txt | sed 's/...//'
## 将 grep root /etc/passwd 查询出的带root的字符中 以root为开始的字符替换为空
$ grep root /etc/passwd | sed 's/^root//'
## (*为正则语法 表示配置*号前面的b字符出现0次或者多次)
$ sed 's/ab*/!/' bfile
## (+号表示匹配一次或者多次)
$ sed -r 's/ab+/!/' bfile
## (?号表示匹配0次或者1次)
$ sed -r 's/ab?/!/' bfile
## (a|b 表示匹配a或者b)
$ sed -r 's/a|b/!/' bfile
## ((aa)|(bb) 表示匹配aa或者bb 多个字符要用括号括起来)
$ sed -r 's/(aa)|(bb)/!/' bfile
$ echo axyzb > cfile
## sed 中()的另外一个作用是 回调(\1 反斜杠1表示前面第一个括号的回调)
$ sed -r 's/(a.*b)/\1:\1' cfile
# 结果: axyzb:axyzb
进阶使用
全局替换
s/old/new/g g为全局替换,用于替换所有出现的次数
s/old/new/n (n为数字 表示出现第几次是进行替换) 配置第n次 进行替换
/如果和正则匹配的内容冲突可以使用其他符号,如: s@old@new@g 、s!old!new!g
标志位
s/old/new/标志位
标志位可以为如下:
数字,第几次出现才进行替换
g,每次出现都进行替换
p打印模式空间的内容。适霜
sed -n ‘script’ filename阻止默认输出
w file将模式空间的内容写入到文件
## 组织默认输出,只输出匹配到的内容
$ head -5 /etc/passwd | sed -n 's/root/!!!!/p'
## 将输出的结果写入到/tmp/a.txt文件中
$ head -5 /etc/passwd | sed -n 's/root/!!!!/w /tmp/a.txt'
默认对每行进行操作,增加寻址后对匹配的行进行操作
/正则表达式/s/old/new/g
行号s/old/new/g
行号可以是具体的行,也可以是最后一行$符号
可以使用两个寻址符号,也可以混合使用行号和正则地址
## 只匹配第一行
$ head -6 /etc/passwd | sed '1s/adm/!/
## 匹配第一行到第三行
$ head -6 /etc/passwd | sed '1,3s/adm/!/
## 匹配第一行到最后一行
$ head -6 /etc/passwd | sed '1,$s/adm/!/
## 只匹配root所在的行
$ head -6 /etc/passwd | sed '/root/s/adm/!/
## 只匹配以bin开头的行
$ head -6 /etc/passwd | sed '/^bin/s/adm/!/
## 只匹配以bin开头的行
$ head -6 /etc/passwd | sed '/^bin/,$s/adm/!/
可以将选项保存为文件,使用-f加载脚本文件
sed -f sedscript filename
分组:
寻址可以匹配多条命令
/regular/{s/old/new/;s/old/new/}
sed 脚本文件
可以将选项保存为文件,使用-f加载脚本文件
sed -f sedscript filename
sed 其他命令
删除命令
[寻址] d 删除模式空间内容,改变脚本的控制流,读取新的输入行
d后的任何指令都不会被执行
追加、插入、更改
追加命令 a
插入命令 i
更改命令 c
读、写
读文件命令 r
写文件命令 w
下一行
下一行命令 n
打印行号命令 =
打印
打印命令 p
退出命令
退出命令 q
## 匹配ab 并在找到的行的前一行添加hello
$ sed '/ab/i hello' bfile
## 匹配ab 并在找到的行的上一行添加hello
$ sed '/ab/a hello' bfile
## 匹配ab 并将找到的行更改为hello
$ sed '/ab/c hello' bfile
## 匹配ab 并将找到的行替换为从afile中读取到内容填充这一行
$ sed '/ab/r afile' bfile
## 将修改的内容保存到gfle 中
$ sed '/ab/r afile' bfile > gfile
## 匹配ab 匹配到后再输出一次
$ sed '/ab/p' bfile
## 只输出匹配的行
$ sed -n '/ab/p' bfile
## 产生1-10000000 的数字每行一个
$ seq 1 10000000 > lines.txt
## 读写1-10行并打印 (次命令将1-10行读取到内存后,继续将11-结尾行读取到内存)
$ time sed -n '1,10p' lines.txt
## 读写1-10行打印并退出 (次命令效率很高) (次命令只讲1-10行读取到内存)
$ time sed -n '10q' lines.txt
sed 多行模式
N将下一行加入到模式空间
D删除模式空间中的第一个字符到第一个换行符
P打印模式空间中的第一个字符到第一个换行符
创建一个1.txt的文件
hel
lo
## 此命令不会匹配到任何内容 (因为文本存储是 会有换行符 \n)
$ sed 'N;s/hello/!!!/' 1.txt
## 匹配到hello
$ sed 'N;s/hel\nlo/!!!/' 1.txt
## 多行模式中可以使用.替换\n
$ sed 'N;s/hel.lo/!!!/' 1.txt
## 创建一个b.txt的文件
$ cat > b.txtx << EOF
> hello
> o bash hel
> lo bash
> EOF
## 每两行 作为输出
$ sed 'N;s/\n//;s/hello bash/hello sed\n/;P;D' b.txt
## 每三行 作为输出
$ sed 'N;N;s/\n//;s/hello bash/hello sed\n/;P;D' b.txt
sed 保持空间
h和H将模式空间内容存放到保持空间 (小写h为覆盖 ,大写H为追加)
g和G将保持空间内容取出到模式空间 (小写g为覆盖 ,大写G为追加)
x交换模式空间和保持空间内容
awk
awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。
(相比较屏幕处理的优点,在处理庞大文件时不会出现内存溢出或是处理缓慢的问题)
(通常,awk是以文件的一行为处理单位的。awk每接收文件的一行,然后执行相应的命令,来处理文本)
语法:awk [-F|-f|-v] ‘BEGIN{} //{command1; command2} END{}’ file
[-F|-f|-v] 大参数,-F指定分隔符,-f调用脚本,-v定义变量 var=value
’ ’ 引用代码块
BEGIN 初始化代码块,在对每一行进行处理之前,初始化代码,主要是引用全局变量,设置FS分隔符
// 匹配代码块,可以是字符串或正则表达式
{} 命令代码块,包含一条或多条命令
; 多条命令使用分号分隔
END 结尾代码块,在对每一行进行处理之后再执行的代码块,主要是进行最终计算或输出结尾摘要信息
输入数据前例程BEGIN{} ## BEGIN 的操作在读取文件之前
主输入循环{}
所有文件读取完成例程 END{}
每行称作AWK的记录
使用空格、制表符分隔开的单词称作字段
可以自己指定分隔的字段
字段的引用
默认情况下,awk 会将如下变量分配给它在文本行中发现的数据字段:
$0 代表整个文本行;
$1 代表文本行中的第 1 个数据字段;
$2 代表文本行中的第 2 个数据字段;
$n 代表文本行中的第 n 个数据字段。
## 输出 以menu开头的行
$ awk '/^menu/{ print $0 }' /boot/grub2/grub.cfg
## 输出 以menu开头的行并按照空格分行 展示分隔后的第二个数据
$ awk -F "'" '/^menu/{ print $2 }' /boot/grub2/grub.cfg
## 输出 以menu开头的行并按照空格分行 展示分隔后的第二个数据 并在前面添加序号
$ awk -F "'" '/^menu/{ print x++; $2 }' /boot/grub2/grub.cfg
赋值操作符
=是最常用的赋值操作符
var1="name"
var2="hello""world"
var3=$1
其他复制操作符
++ – += -= *= /= %= ^=
算数操作符
-
-
- / % ……
-
系统变量
FS和OFS字段分隔符,OFS表示输出的字段分隔符, RS记录分隔符
NR和FNR行数
NF字段数量,最后一个字段内容可以用$NF取出
##
$ head -5 /etc/passwd | awk 'BEGIN{FS=":"}{print $1}'
## 等同于上面的效果
$ head -5 /etc/passwd | awk -F ":" '{print $1}'
## 输出 以:分隔的第一个字符和第二个字符
$ head -5 /etc/passwd | awk 'BEGIN{FS=":"}{print $1,$2}'
## 输出 以:分隔的第一个字符和第二个字符 并使用-将字符连接
$ head -5 /etc/passwd | awk 'BEGIN{FS=":";OFS="-"}{print $1,$2}'
## 以:作为行的分隔符输出内容
$ head -5 /etc/passwd | awk 'BEGIN{RS=":"}{print $0}'
## 显示行号(NR)和内容($0)
$ head -5 /etc/passwd | awk '{print NR,$0}'
## 显示多个文件的行号和内容 (FNR 行号会重置)行号会在第二个文件输出时重置(重新重1开始)
$ awk '{print FNR,$0}' /etc/hosts /etc/hosts
## 显示多个文件的行号和内容 NR行号连续
$ awk '{print NR,$0}' /etc/hosts /etc/hosts
## 以:为分隔符统计每行的数量
$ head -5 /etc/passwd | awk 'BEGIN{FS=":"}{print NF}'
条件语句
if判断:
条件语句使用if开头,根据表达式的结果来判断执行哪条语句
if(表达式)
awk语句1
[else
awk语句2
]
如果有多个语句需要执行可以使用{}将多个语句括起来
while循环:
while(表达式)
awk语句1
do循环:
do{
awk 语句①
}while(表达式)
for循环:
for(初始值;循环判断条件;累加)
awk语句1
影响控制的其他语句:
break
continue
##
$ awk '{if($2>=80) print $1}' kpi.txt
##
$ awk '{if($2>=80) {print $1 ; print $2} }' kpi.txt
## NF 表数字数量的最大值 输出
$ head -1 kpi.txt | awk '{for(c=2;c<=NF;c++) print $c} '
# 结果:
# 70
# 72
# 74
# 76
# 74
# 72
## 输出第一行的总和
$ head -1 kpi.txt | awk '{for(c=2;c<=NF;c++) sum+=$c; print sum} '
## 输出第一行的平均值
$ head -1 kpi.txt | awk '{for(c=2;c<=NF;c++) sum+=$c; print sum/(NF-1)} '
## 输出每一行的平均值
$ awk '{sum=0;for(c=2;c<=NF;c++) sum+=$c; print sum/(NF-1)} '
制作加载awk脚本
制作: 将awk命令复制到.awk文件中
加载: awk -f avage.awk kpi.txt
awk数组
数组的定义: 数组名[下标] =值
数组的遍历: for(变量 in 数组名) 使用数组名[变量]的方式依次对每个数组进行操作
删除数组: delete 数组[下标]
命令行参数数组
## 输出每一行的平均值 average[$1] 表示每行用户的名称
[ root@ server ~]# awk '{ sum=0; for(column=2; column<=NF; column++) sum+=$column; average[$1]=sum/(NF-1)} END {for(user in average) print user, average[ user]}'
## 输出所有的平均值
[ root@ server ~]# awk '{ sum=0; for(column=2; column<=NF; column++) sum+=$column; average[$1]=sum/(NF-1)} END {for(user in average) sum2+=average[ user] ;print sum2/NR }'
命令行参数数组
ARGC awk 命令后面所带参数的个数 (ARGC 命令行参数的数目)
ARGV awk 命令后面所带参数具体每个参数的内容 (包含命令行参数的数组)
创建arg.awk文件
BEGIN{
for(x=0;X<ARGC;x++)
print ARGV[x]
print ARGC
}
执行arg.awk文件
##
$ awk -f arg.awk 11 22 33
# 结果:
# awk (ARGV[0] 命令的名称)
# 11
# 22
# 33
# 4 (ARGC 命令的个数)
awk 数组功能的使用
创建 result.awk
{
sum=0
for(column=2;column <=NF;column++)
sum+=$column
average[$1]=sum/(NF-1)
print $1,average[$1]
if(average[$1]>=80)
letter ="S"
else if( average[$1]>=70)
letter ="A"
else if( average[$1]>=60)
Letter ="B"
else
letter="C"
print $1,average[$1],letter
letter_all[letter]++
}
END{
for( user in average)
sum_all +=average[user]
avg_all=sum_all /NR
print "average all:", avg._all
for( user in average)
if( average[user]>avg_all)
above++
else
below++
print "abovel",above
print "below",below
print "s:",letter_alL["S"]
print "A:",letter_alL["A"]
print "B:",letter_alL["B"]
print "c:",Letter_alL["C"]
执行 result.awk
$ awk -f result.awk kpi.txt
结果:
awk 函数
rand() 随机数
srand() 重新获取种子
gsub(r,s,t)
index(s,t) 从字符串中取值
length(s) 字符串长度
match(s,r) 字符串匹配
split(s,a,sep) 字符串分隔
sub(r,s,t)
substr(s,p,n)
## 输出随机数 (不重复的随机数)
$ awk 'BEGIN{srand();print rand()}'
## awk 'BEGIN{print rand()}' 输出的随机数是重复的
$ awk 'BEGIN{print rand()}'
自定义函数
function 函数名(参数){
awk语句
return awk变量
}