首页 > 技术文章 > sed和awk

demian 2021-04-01 23:40 原文

sed

sed(Stream Editor) 流编辑器,用来处理文件的

sed是一行一行读取文件内容并按照要求进行处理,把处理后的结果输出到屏幕

  1. 首先sed读取文件中的一行内容,把其保存在一个临时缓存区中(也称为模式空间)
  2. 然后根据需求处理临时缓冲区中的行,完成后把该行发送到屏幕上

总结:

  1. 由于sed把每一行都存在临时缓冲区中,对这个副本进行编辑,所以不会直接修改源文件
  2. sed主要用来自动编辑一个或多个文件,简化对文件的反复操作,对文件进行过滤或转换操作

sed使用方法

sed常见的语法格式有两种,一种叫命令行模式,另一种叫脚本模式

命令行格式

sed [options] '处理动作' 文件名

-e  进行多项(多次)编辑
-n  取消默认输出  不自动打印模式空间
-r  使用扩展正则表达式
-i  原地编辑(修改源文件)
-f  指定sed脚本的文件名

常见处理动作

以下所有的动作都必须在单引号里

'p'  打印
'i'  在指定行之前插入内容  类似vim里的大写O
'a'  在指定行之后插入内容  类似vim里的小写o
'c'  替换指定行所有内容
'd'  删除指定行

对文件进行增、删、改、查操作

语法:sed选项 '定位+命令' 需要处理的文件

1)打印文件内容

sed '' a.txt # 对文件什么都不做
sed -n 'p' a.txt # 打印每一行,并取消默认输出
sed -n '1p' a.txt # 打印第1行
sed -n '2p' a.txt # 打印第2行
sed -n '1,5p' a.txt # 打印1到5行
sed -n '$p' a.txt # 打印最后1行
sed -n '/root/p' 1.txt #打印包含root的行

2)增加文件内容

i 地址定位的上面插入

a 下面插入

sed '$a99999' a.txt  文件最后一行下面增加内容
sed 'a99999' a.txt  文件每行下面增加内容
sed '5a99999' a.txt  文件第5行下面增加内容
sed '$i99999' a.txt  文件最后一行上一行增加内容
sed 'i99999' a.txt  文件每行上一行增加内容
sed '6i99999' a.txt 文件第6行上一行增加内容
sed '/^uucp/ihello' a.txt  以uucp开头行的上一行插入内容
# 注意 如果要需要连续插入两行有两种方法
# (1) 利用\n
# (2) 输入 \ 然后回车输入内容
  1. 修改文件内容

c 替换指定的整行内容

sed '5chello world' a.txt  替换文件第5行内容
sed 'chello world' a.txt  替换文件所有内容
sed '1,5chello world' a.txt  替换文件1到5行内容为一行hello world
sed '/^user01/c888888' a.txt 替换以user01开头的行
  1. 删除文件内容
sed '1d' a.txt  删除文件第1行
sed '1,5d'  a.txt  删除文件1到5行
sed '$d'  a.txt  删除文件最后一行
sed -r '/正则匹配/d'

对文件进行搜索替换操作

语法:sed 选项 's/搜索的内容/替换的内容/动作' 需要处理的文件

其中,s表示search搜索,斜杠/表示分隔符,可以自己定义;动作一般是打印p和全局替换g(不加默认只替换第一个)

sed -n 's/root/ROOT/p' 1.txt  # 只替换第一个root
sed -n 's/root/ROOT/gp' 1.txt
sed -n 's/^#//gp' 1.txt # 将#开头的行删除
sed -n 's@/sbin/nologin@itcast@gp' a.txt # 将/sbin/nologin@itcast替换成itcast  @为自定义分隔符
sed -n 's/\/sbin\/nologin/itcast/gp' a.txt
sed -n '10s#/sbin/nologin#itcast#p' a.txt # 只替换第10行的内容
sed -n 's@/sbin/nologin@itcast@p' 2.txt # 注意:搜索替换中的分隔符可以自己指定
sed -n '1,5s/^/#/p' a.txt # 注释掉文件的1-5行内容
sed -n 's/\(10.1.1.\)1/\1254/gp' a.txt #将10.1.1.1替换成10.1.1.254 其中后面的\1代表前面的10.1.1.

其他命令

r		# 从另外文件中读取内容
w   # 内容另存为    sed '1,3w new.txt' 1.txt
&   # 保存查找串以便在替换串中引用  和\(\)相同  
    sed -n 's/^sync/#&/gp' 1.txt  将sync开头的那一行注释掉, 用&引用^sync
    sed -n 's\(^sync\)/#\1/gp' 1.txt  与上一行等价
=   # 打印行号
    sed -ne '/root/p' -ne '/root/=' 1.txt  # e表示多次编辑
    sed -n '/root/=;/root/p' 1.txt
!   # 对所选行以外的所有行应用命令,放到行数之后
q   # 退出    处理到这一行退出

其他选项

-e 多项编辑
-r 扩展正则
-i 修改原文件
sed -ne '/root/=' -ne '/root/p' 1.txt
sed -e '5ihello world' -e '8a哈哈哈哈' 1.txt -e '5=;8='

# 过滤/etc/vsftpd/vsftpd.conf文件中以#开头和空行:
grep -Ev '^#|^$' 1.txt
sed -e '/^#/d' -e '/^$/d' 1.txt
sed '/^#/d;/^$/d' 1.txt
sed -r '/^#|^$/d' 1.txt

过滤smb.conf文件中生效的行:
sed -e '/^#/d' -e '/^;/d' -e '/^$/d' -e '/^\t$/d' -e '/^t#/d' 1.txt
sed -r '^(#|$|;|\t#|\t$)/d' 1.txt

grep '^[^a-z]' 1.txt
sed -n '/^[^a-z]/p' 1.txt

# 过滤出文件中的ip地址
grep -E '([0-9]{1,3}\.){3}[0-9]{1,3}' 1.txt
sed -nr '/([0-9]{1,3}\.){3}[0-9]{1,3}/p' 1.txt

ifconfig | sed -nr '/([0-9]{1,3}\.)[0-9]{1,3}/p' | head -1|sed -r 's/([a-z:]|[A-Z\t])//g' |
sed 's/ /\n/g' | sed '/^$/d'

ifconfig eth0 | sed -n '2p' | sed -n 's/.*addr:\(.*\) Bcast:\(.*\) Mask:\(.*\)/\1\n\2\n\3/p'
-i 选项 直接修改原文件
sed -i 's/root/ROOT;s/stu/STU/' 1.txt
sed -i '1,5s/^/#&' a.txt # 加注释
注意:
-n 不要与-i一起使用
p命令 不要与-i一起使用

sed结合正则使用

sed 选项 'sed命令或者正则表达式或者地址定位' 文件名

  1. 地址用于决定对哪些行进行编辑,地址的形式可以是数字、正则表达式、或两者的结合
  2. 如果没有地址定位,sed将处理输入文件的所有行
/key/  查询包含关键字的行  sed -n '/root/p' 1.txt
/key1/,/key2/  匹配包含两个关键字之间的行  sed -n '/^adm/,/^mysql/p' 1.txt
/key/,x  从匹配关键字的行开始到文件第x行之间的行(包含关键字所在行) sed -n '/^ftp/,7p'
x,/key/  从文件第x行开始到与关键字的匹配行之间的行 
x,y!  不包含x到y的行
/key/!  不包括关键字的行  sed -n '/bash$/!p' 1.txt

脚本格式

用法

# sed -f scripts.sh file   使用脚本处理文件
建议使用  ./sed.sh  file

脚本的第一行写上
#!/bin/sed -f
1,5d
s/root/hello/g
3i777
5i888
a999
p
注意事项
1) 脚本文件是一个sed的命令行清单,'coomands'
2) 在每行的末尾不能有任何空格,制表符(tab)或其他文本
3) 如果在一行中有多个命令,应该用分号分隔
4) 不需要且不可用引号保护命令
5) #号开头的行为注释

例子:
#!/bin/sed -f
2a\
*********************
2,$s/stu/user/
$a\
we insert a new line
s/^[a-z].*/#&/

#!/bin/sed -f
3a*********************
$chelloworld
1,3s/^/#&/

awk

awk是一种编程语言,主要用在linux/unix下对文本和数据进行处理,是linux/unix下的一个工具,数据可以来自标准输入,一个或多个文件,或其他命令的输出。

awk的处理文本和数据的方式:逐行扫描文件,默认从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作

gawk是awk的GNU版本,它提供了Bell实验室和GNU的一些扩展,linux系统中已经把awk链接到gawk

作用

  1. awk用来处理文件和数据,是类unix下的一个工具,也是一种编程语言
  2. 可以用来统计数据,比如网站的访问量,访问的IP量等等
  3. 支持条件判断,支持for和while循环

awk的使用方式

命令行模式使用

awk 选项 '命令部分' 文件名

特别说明:
引用shell变量需要用双引号引起
luck_line=123456
sed -i "/$luck_line/d" file

常用选项

  • -F 定义字段分割符号,默认的分隔符是空格
  • -v 定义变量并赋值

命名部分说明

  • 正则表达式,地址定位
'/root/{awk语句}'   sed中:'/root/p'
'NR==1,NR==5{awk语句}'   sed中:'1,5p'
'/^root/,/^ftp/{awk语句}'   sed中:'/^root/,/^ftp/p'
  • {awk语句1;awk语句2;...}
'{print $0;print $1}'    sed中:'p'
'NR==5{print $0}'    sed中:'5p'
注:awk命令语句间用分号间隔
  • BEGIN(文件处理前)...END(文件处理后)...
'BEGIN{awk语句};{处理中};END{awk语句}'
'BEGIN{awk语句};{处理中}'
'{处理中};END{awk语句}'

脚本模式使用

脚本编写

#!/bin/awk -f  定义魔法字符
以下是awk引号里的命令清单,不要用引号保护命令,多个命令用分号间隔
BEGIN{FS=":"}
NR==1,NR==3{print $1"\t"$NF}
...

脚本执行

方法1:
awk 选项 -f awk的脚本文件 要处理的文本文件
awk -f awk.sh filename

sed -f sed.sh -i filename

方法2:
./awk的脚本文件(或者绝对路径)  要处理的文件
./awk.sh filename
./sed.sh filename

awk内部相关变量

$0             当前处理行的所有记录
$1,$2,$3...$n  文件中每行以间隔符号分割的不同字段       awk -F: '{print $1,$3}' # :分隔
NF             当前记录的字段数(列数)               awk -F: '{print NF}'
$NF            最后一列                            $(NF-1)表示倒数第二列
FNR/NR         行号   用||(第1和第5行) 和,(1到5行)NR>=1 && NR<=5 连接
FS             定义间隔符                          'BEGIN{FS=":"};{print $1,$3}'
OFS            定义输出字段分隔符,默认空格            'BEGIN{OFS="\t"};{print $1,$3}'
RS             输入记录分隔符,默认换行               'BEGIN{RS="\t"};{print $0}'
ORS            输出记录分隔符,默认换行               'BEGIN{ORS="\n\n"};{print $1,$3}'

常用内置变量举例

awk -F: '/root/{print $1,$NF}' 1.txt # 匹配到包含root的行以:分隔的第一列和最后一列
awk 'NR==1,NR==5;/^root/{print $0}' 1.txt
#;前第一到第五行都要打印,后面开头为root的再打印一遍,逐行处理
awk 'BEGIN{FS=":";OFS="@@@@"};{print $1,$NF}' 1.txt
awk 'BEGIN{FS=":"};{print "用户名是"$1"@@@@@@@@@@"$NF}' 1.txt
awk -F: 'BEGIN{RS="\t"};{print $1}' 1.txt  #将输入中\t分隔的看做一行

awk工作原理

awk -F: '{print $1,$3}' 1.txt

  1. awk使用一行作为输入,并将这一行赋给内部变量$0,每一行也可以称为一个记录,以换行符(RS)结束

  2. 每行被间隔符:(默认为空格或制表符)分解成字段(或域),每个字段存储在已编号的变量中,从$1开始

    问:awk如何知道用空格来分隔字段的呢?

    答:因为有一个内部变量FS来确定字段分隔符,初始时FS赋为空格

  3. awk使用print函数打印字段,打印出来的字段会以空格分隔,因为$1,$3之间有一个逗号,逗号比较特殊,它映射为另一个内部变量,称为输出字段分隔符OFS,OFS默认为空格

  4. awk处理完一行后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理,该过程将处理所有行处理完毕

awk进阶

  1. 格式化输出print和printf
print函数 类似echo "hello world"
data | awk '{print "Month: "$2 "\nYear: "$NF}' /etc/passwd # 默认以空格分隔

printf函数 类似echo -n "hello world"
awk -F: '{printf "%-15s %-10s %-15s\n", $1,$2,$3}' /etc/passwd

%s 字符类型  strings  %-20s
%d 数值类型
占15字符
- 表示左对齐,默认是右对齐
printf默认不会在行尾自动换行,要加\n
  1. awk变量定义
awk -v NUM=3 -F: '{print NUM}' /etc/passwd # 会输出文件行数这么多行的3
awk -v num=1 'BEGIN{print $num}'
注意:
awk中调用定义的变量不需要加$, 加了相当于$0 $1 $2这种分隔后的字段而不是定义的变量值
  1. awk中BEGIN..END使用

BEGIN:表示在程序开始前执行

END:表示所有文件处理完后执行

用法:'BEGIN{开始处理前};{处理中};END{处理结束后}' # 不用同时存在

  1. awk和正则的综合运用
==		等于
!=		不等于
>			大于
<			小于
>=		大于等于
<=		小于等于
~			匹配
!~		不匹配
!			逻辑非
&&		逻辑与   逻辑运算符用来连接表达式
||		逻辑或
awk -F: '/^lp/,NR==10{print $0}' passwd  # 打印从以lp开头到文件第10行
awk -F: '/^root/ || /^lp/{print $0}' # 打印以root和lp开头的这两行
awk -F: '/^root/;/^lp/{print $0}'  # ;分隔命令,默认打印 效果同上

awk 'NR==1,NR==5;/^root/{print $0}' 1.txt # 打印1到5行,且以root开头的被多打印一遍,一行一行处理
打印1-5行以root开头的行
awk 'NR>=1 && NR<=5 && $0 ~ /^root/{print $0}' 1.txt # 匹配
awk 'NR>=1 && NR<=5 && /^root/{print $0}'
ifconfig eth0| grep Bcast|awk -F'[: ]+' '{print $4}' # 分隔符设为:和空格(一个或多个)
ifconfig eth0| awk -F"[: ]+" '/inet addr/{print $4RS$6}'

流程控制语句

if 结构

if语句:

if [ xxx ];then
xxx
fi

格式:
awk 选项 '正则,地址定位{awk语句}' 文件名
{ if(表达式) {语句1;语句2;...} }
awk -F: '{if($3>=500 && $3<=60000)} {print $1,$3}' passwd
awk -F: '{if($3==0) {print $1"是管理员"}}' passwd
awk 'BEGIN{if($(id -u)==0) {print "admin"} }'

if...else结构

if...else语句
if [ xxx ];then
   xxxx
else
   xxxx
fi

格式:
{if(表达式) {语句;语句;...} else {语句;语句;...}}

awk -F: '{ if($3>=500 && $3!=65534) {print $1"是普通用户"} else {print $1,"不是普通用户"}}' passwd

if...elif...else语句

if [ xxx ]
	xxxx
elif [ xxx ]
	xxxx
...
else
	xxxx
fi

if...else if...else语句
for ((i=1;i<=5;i++));do echo $i;done

格式:
{ if(表达式1) {语句;语句;...} else if(表达式2) {语句;语句;...} else if(表达式3) {语句;语句;...} else {语句;语句;...} }
awk 'BEGIN {for(i=1;i<=5;i++){print i}}
计算1-5的和
awk 'BEGIN{for(i=1;i<=5;i++)(sum+=i);{print sum}'

while循环

打印1-5
i=1;while (($i<=5));do echo $i;let i++;done
awk 'BEGIN{i=1;while(i<=5){print i;i++}}'
打印1-5的和
awk 'BEGIN{i=1;while(i<=5){sum+=i;i++};print sum}' # 定义变量默认为0

嵌套循环

for ((y=1;y<=5;y++))
do
	for ((x=1;x<=$y;x++))
		do
			echo -n $x
		done
echo
done

awk 'BEGIN{for(y=1;y<=5;y++){for(x=1;x<=y;x++){printf x};print}}'

awk算数运算

+ - * / % ^
可以在模式中执行计算,awk都将按浮点数方式执行算术运算
awk 'BEGIN{print 1+1}'
awk 'BEGIN{print 2**3}'
awk 'BEGIN{print 2/3}'

awk统计案例

  1. 统计系统中各种类型的shell
  • awk中关联数组不需要声明
awk -F: '{ shell[$NF]++};END{for(i in shells) {print i,shells[i]}}' /etc/passwd

推荐阅读