sed和awk
sed
sed(Stream Editor) 流编辑器,用来处理文件的
sed是一行一行读取文件内容并按照要求进行处理,把处理后的结果输出到屏幕
- 首先sed读取文件中的一行内容,把其保存在一个临时缓存区中(也称为模式空间)
- 然后根据需求处理临时缓冲区中的行,完成后把该行发送到屏幕上
总结:
- 由于sed把每一行都存在临时缓冲区中,对这个副本进行编辑,所以不会直接修改源文件
- 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) 输入 \ 然后回车输入内容
- 修改文件内容
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开头的行
- 删除文件内容
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命令或者正则表达式或者地址定位' 文件名
- 地址用于决定对哪些行进行编辑,地址的形式可以是数字、正则表达式、或两者的结合
- 如果没有地址定位,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
作用
- awk用来处理文件和数据,是类unix下的一个工具,也是一种编程语言
- 可以用来统计数据,比如网站的访问量,访问的IP量等等
- 支持条件判断,支持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'
'{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
-
awk使用一行作为输入,并将这一行赋给内部变量$0,每一行也可以称为一个记录,以换行符(RS)结束
-
每行被间隔符:(默认为空格或制表符)分解成字段(或域),每个字段存储在已编号的变量中,从$1开始
问:awk如何知道用空格来分隔字段的呢?
答:因为有一个内部变量FS来确定字段分隔符,初始时FS赋为空格
-
awk使用print函数打印字段,打印出来的字段会以空格分隔,因为$1,$3之间有一个逗号,逗号比较特殊,它映射为另一个内部变量,称为输出字段分隔符OFS,OFS默认为空格
-
awk处理完一行后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理,该过程将处理所有行处理完毕
awk进阶
- 格式化输出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
- awk变量定义
awk -v NUM=3 -F: '{print NUM}' /etc/passwd # 会输出文件行数这么多行的3
awk -v num=1 'BEGIN{print $num}'
注意:
awk中调用定义的变量不需要加$, 加了相当于$0 $1 $2这种分隔后的字段而不是定义的变量值
- awk中BEGIN..END使用
BEGIN:表示在程序开始前执行
END:表示所有文件处理完后执行
用法:'BEGIN{开始处理前};{处理中};END{处理结束后}' # 不用同时存在
- 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统计案例
- 统计系统中各种类型的shell
- awk中关联数组不需要声明
awk -F: '{ shell[$NF]++};END{for(i in shells) {print i,shells[i]}}' /etc/passwd