awk sed处理文件操作
awk、sed对处理文件和写shell脚本时非常有益,日常工作中也会经常用到,很有必要掌握。
awk
awk '条件{命令}' filename
假设一个待处理的文件test 或者 netstat -anp的管道结果 是这样的:
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:5088 0.0.0.0:* LISTEN 430/agent
tcp 0 0 127.0.0.1:1988 0.0.0.0:* LISTEN -
tcp6 0 0 :::9568 :::* LISTEN 773/java
tcp6 0 0 :::5288 :::* LISTEN 3749/plugin
udp 0 0 0.0.0.0:29251 0.0.0.0:* 430/agent
unix 3 [ ] STREAM CONNECTED 705066939 - /var/run/xxx-agent.sock
条件+动作
需要第一列是tcp的,并打印出第4列和第6列
$ netstat -anp | awk '/^tcp[^0-9]/ {print $1,$4,"\t",$6}'
或者
$ netstat -anp | awk '$1 == "tcp" {print $4,"\t",$6}'
BEGIN和END
比如我想在刚才打印的两列上边,写个头。比如4列头是aaa,6列头是bbb。4列尾是ccc,6列尾是ddd
$ netstat -anp | awk 'BEGIN {print "aaa","\t","bbb"} /^tcp/ {print $4,"\t",$6} END {print "ccc","\t","ddd"}'
值得注意的是,awk是以文件每一行进来处理的。不难发现,语法中单引号里头,都是按照"条件+{动作}“这种格式来的。
- BEGIN是条件, {print “aaa”,"\t”,“bbb”}是动作;BEGIN是在文件处理第一行开始前先执行的。
- /^tcp/ 是条件,{print $4,"\t",$6}是动作;
- END是条件,{print “ccc”,"\t",“ddd”}是动作。END是在文件处理结束以后,执行的。
当一个动作{A}前边紧接着还是个{动作X},就意味着A这个动作没有条件,就是指什么条件都可以。
awk内置变量
awk中几个内置变量很常用。
- F 字段 Field
- FS 分隔符号
- NR awk读取的记录数(awk是一行一行的读取) | number of records 原文件记录的第几行数
- NF 表示读取的列数 | number of fields 该行记录的总列数
举个例子:用户文件/etc/passed是以冒号分隔的
$ awk '{FS=":"}{print $1,"\t",$2}' /etc/passwd | head -n5
# 结果
root:x:0:0:root:/root:/bin/bash
bin x
daemon x
adm x
lp x
发现第一行并没有得到我们想要的效果。这是因为{FS=":"}这个命令前边并没有条件限制,而此时awk已经把第一行读完了。
这里就需要在{FS=":"}这个命令的前边加上BEGIN关键字就好了。
比如 要获取偶数行的信息,就可以利用NR
$ awk 'BEGIN{FS=":"} NR%2==0 {print NR,$1,"\t",$2}' /etc/passwd
前边的NR、NF是awk的内置变量。awk也可以自定义变量,awk编程。
比如上边的/etc/passwd中有多少个用户(也是多少行…)并且输出每个用户名
利用awk编程实现可以这样:
$ awk -F ':' '{num++; print $1;} END {print "total is ", num}' /etc/passwd
{ }中两个命令使用分号 ; 分隔。
输出的分隔符,比如输出的列用逗号分割,利用OFS
$ awk -F',' 'BEGIN{OFS=","} {print $2,$6,$9}' xx.log
再举个例子:找出文件xxx.txt中不是以#开头的所有行
# if放到命令里
$ awk '{if($1!~/^#/) print $0}' xxx.txt
sed
sed的命令(stream editor 流编辑器),同样是对行的处理。注意要匹配什么。
关键字 | 含义 |
---|---|
s | 替换,重点是匹配什么 |
d | 删除,用数字表示行,或者也利用匹配行 |
i | 行前插入 |
a | 行后追加 |
c | 替换整个行 |
删除
# 比如删除2到4行的内容
$ sed '2,4d' filename
# 从第二行开始都删除
$ sed '2,$d' filename
# 删除以#开头的行
$ sed '/^#/ d' filename
# 清除一个文件的空行
$ sed '/^$/d' test.log > test1.log
替换
# 数字替换成xx
$ sed 's/[0-9]/xx/g'
# 替换8到10行的内容,把其中每行出现的udp替换成tcp
$ sed '8,10s/udp/tcp/g' filename
# 只替换每行出现的第一个
$ sed 's/udp/tcp/1' filename
插入
# 在第一行插入字符串hehe
$ sed '1 i hehe' filename
# 在原来的第2行到第4行上,都插入xxx
$ sed '2,4 i xxx' filename
# 在第2行后面追加字符串hi,换行
$ sed '2 a hi' filename
# 在原来的第2行到第4行上,都追加xxx
$ sed '2,4 a xxx' filename
获取
取出一个文件a.log的2到12行内容
$ sed -n '2,12p' a.log
sed 对正则中一些特殊含义字符需要转义才行,比如+ ()等
举例:查出当前目录下所有文件的后缀名 () 需要转义 +符号也转义,
# 这里 第一个括号是文件名,第二个括号是后缀名,把整个文件名替换成第二个括号里的名字
$ ls -F | grep -v '/'| sed 's/\(.*\)\.\(.\+\)$/\2/' | sort | uniq