文本处理工具sed和awk的使用


一、文本处理三剑客
grep:文本过滤工具,详见博客http://9124573.blog.51cto.com/9114573/1697694
sed:行编辑工具
awk:文本报告生成器
 
二、sed
sed(stream editor):行编辑工具,能够完美地配合正则表达式使用。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕,接着处理下一行,这样不断重复
 
用法:sed [option]... '地址COMMAND’ FILE...
例如 sed -i '/^[[:upper:]]/d' /etc/fstab #删除/etc/fstab中以大写字母开头的行
①地址:
行范围:
start_line,end_line
/pattern1/,/pattern2/:第一次被pattern1匹配到的行开始,到第一次被pattern2匹配到的行结束之间的所有行;
特定行:
/pattern/
#
无地址:全文
 
②编辑命令:
p:打印,默认情况sed把所有行都打印到屏幕,如果某行匹配到模式,则把该行另外再打印一遍
d:删除
i \text:在匹配行的上方插入(insert)text
a \text:在匹配行的下方附加(attach)text
r /path/to/somefile:从指定的文件中读行并附加到匹配行的下方
w /path/to/some_file: 把符合条件的行保存至指定的文件中
=:显示符合条件行的行号
s/地址/要替换成的新内容/[gi],或者s@@@
g:global,全文替换
i:ignore,忽略字符大小写
!COMMAND:取反
选项:
-n:取消自动显示模式空间
-r:支持扩展正则表达式
-i:修改原文件
-e:使用该选项可同时执行多个编辑操作
 sed -e '地址1COMMAND1' -e '地址2COMMAND2'... 
 ▲注意:
①sed默认显示模式空间,意思就是:若行被模式匹配到,则显示其被处理后的结果;若未被匹配到,也显示其原本的内容,而非不显示。使用选项-n可取消自动显示模式空间
②sed默认不修改原文件,使用-i选项可处理原文件
 
sed使用示例:
①只打印/etc/fstab中第2行到第5行的内容。
sed -n '2,5p' /etc/fstab

[root@node2 ~]# sed -n '2,5p' /etc/fstab
#
# /etc/fstab
# Created by anaconda on Thu Aug6 04:30:38 2015
#
[root@node2 ~]# sed '2,5p' /etc/fstab

#
#
# /etc/fstab
# /etc/fstab
# Created by anaconda on Thu Aug6 04:30:38 2015
# Created by anaconda on Thu Aug6 04:30:38 2015
#
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
UUID=aa0330af-3681-428c-98e2-ccf2e6f0f686 / ext4defaults 1 1
UUID=cc46ae66-0720-4c17-89de-ef0d5b72da60 /bootext4defaults 1 2
UUID=24de94c2-8c13-4f50-aeb9-82d0b35d654f swapswapdefaults 0 0
tmpfs/dev/shm tmpfs defaults 0 0
devpts /dev/pts devptsgid=5,mode=6200 0
sysfs/syssysfs defaults 0 0
proc/procprocdefaults 0 0

 

②删除/etc/fstab原文件中的以大写字母开头的行,并在第3行之前插入"# modified"
 sed -i -e '/^[[:upper:]]/d' -e '3i \# modified' /etc/fstab

[root@node2 ~]# sed -i -e '/^[[:upper:]]/d' -e '3i \# modified' /etc/fstab
[root@node2 ~]# cat /etc/fstab #原文件已被修改

#
# modified
# /etc/fstab
# Created by anaconda on Thu Aug6 04:30:38 2015
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
tmpfs/dev/shm tmpfs defaults 0 0
devpts /dev/pts devptsgid=5,mode=6200 0
sysfs/syssysfs defaults 0 0
proc/procprocdefaults 0 0

 

③echo一个路径给sed,通过sed取出其目录名;例如echo "/etc/sysconfig/" | sed,返回/etc
echo '/etc/sysconfig/' | sed 's@[^/]\{1,\}/\?$@@'
 
三、awk
 awk是一款强大的编程工具,用于在linux/unix下对处理文本。awk功能较grep和sed强大得多,它可以实现对文件中每一行内容的每个字段分别进行格式化,而后进行显示,且支持使用变量(内置变量、自定义变量)、循环、条件、数组等;但鉴于awk的特殊工作机制,若grep和sed能完成的操作,就尽量不要让awk去完成
 在linux中,awk实际上是gawk(GNU awk),只是为了迎合用户使用习惯,linux给/bin/gawk建立了一个软链接/bin/awk
 工作机制:awk以行为单位处理,它将读到的行内容赋给变量$0,然后按照默认或指定的分隔符(分隔符保存在变量FS中)对其进行切片,每段存储在从$1开始的变量中,可供引用或格式化输出
 
基本用法:awk [options] 'program' file...
 awk [option]... 'PATTERN{action1;action2...}' FILE...
常用选项:
-F fs:指定输入分隔符
-v var_name=VALUE:自定义变量
-f scriptfile:加载指定文件中的awk脚本
 awk -f scriptfile FILE...
 
[root@node2 ~]# cat awk.txt
$3>=500{print $1,$3}
$7~/nologin$/{print $1}
[root@node2 ~]# awk -F: -f awk.txt /etc/passwd
...
wittgenstein 500
dhcpd
apache
tesla 501
 
1、PATTERN:
 行范围:
start_line, end_line
/pattern1/,/pattern2/
 特定行:
/pattern/
#
 表达式:
>, >=, ==, <, <=, !=, ~(模式匹配),!~
 BEGIN模式:在输入流开始之前执行一次
例如 awk 'BEGIN{action2}PATTERN{action1}'表示在action1之前执行一次action2
 END模式:从输入流中读完所有的行之后执行一次
awk 'PATTERN{action1}END{action2}'
 PATTERN为空:全文
 
2、常用的action
①Expressions
②Control statements
③Compound statements
④input statements
⑤output statements
 
3、变量
①内置变量:
NF:Number of Field,字段数
NR:number of record,行号;所有文件一并计数
FNR:行号,各文件分别计数;
FS: Field Seperator,输入时的字段分隔符,默认为空格
指定输入时的字段分隔符的两种方式:
 awk -F: 'PATTERN{action}
 awk 'BEGIN{FS=":"}PATTERN{action}' 
RS:Record Seperator, 输入行分隔符
OFS:输出时的字段分隔符,默认为空格
ORS:ORS: Outpput Row Seperator, 输出时的行分隔符
ARGV:数组,保存awk命令本身和参数;例如 awk '{print $0}' 1.txt 2.txt:ARGV[0]保存"awk",ARGV[1]保存"1.txt",ARGV[2]保存"2.txt",ARGC为3
ARGC:保存awk命令和参数的总个数
FILENAME: awk正在处理的当前文件的名称
②自定义变量
-v var_name=VALUE
变量名区分字符大小写;
(1)可以在script中定义变量;
(2)可以在命令行中通过-v选项自定义变量;
示例:
awk 'BEGIN{a="hello";print a}'
awk -v a="hello" 'BEGIN{print a}'
★在awk中引用变量的值,不需要以$开头,有以$开头的变量,是用于引用字段的值
 
[root@node2 ~]# awk 'BEGIN{FS=":";OFS=":"}{print $1,$7}' /etc/passwd
root:/bin/bash
bin:/sbin/nologin
daemon:/sbin/nologin
adm:/sbin/nologin
lp:/sbin/nologin
sync:/bin/sync
shutdown:/sbin/shutdown
halt:/sbin/halt
...
 
4、awk的输出
print item1[,item2,...]
要点:
①各项目之间使用逗号分隔,而输出时则使用输出分隔符分隔;
②输出的各item可以是字符串或数值、当前记录的字段、变量或awk的表达式;数值会被隐式转换为字符串后输出;
③print后面item如果省略,相当于print $0;输出空白,使用pirnt "";
 
5、awk的printf命令
用法:printf format, item1, item2,...
要点:
 ①要指定format,format用于为后面的每个item指定其输出格式
 ②不会自动换行,如需换行则需要给出\n
 
format格式的指示符都以%开头,后跟一个字符:
 %c:显示字符的ASCII码;
 %d, %i:十进制整数;
 %e, %E:科学计数法显示数值;
 %f:显示浮点数,默认保留六位小数,四舍五入
 %g, %G:以科学计数法格式或浮点数格式显示数值;
 %s:显示字符串;
 %u:显示无符号整数;
 %%:显示%自身;
修饰符:
#:显示宽度
-:左对齐,默认为右对齐
+:显示数值的符号
.#: 取值精度
示例:
 awk -F: '{printf "%15s,%-20s\n",$1,$7}' /etc/passwd
指定输入分隔符为冒号,显示第一、第七字段,位宽分别为15和20,且第七字段左对齐
 awk 'BEGIN{printf "%15.2f\n",3.1415926}'
 
[root@node2 ~]# awk -F: '{printf "%15s,%-20s\n",$1,$7}' /etc/passwd
 root,/bin/bash 
bin,/sbin/nologin
daemon,/sbin/nologin
adm,/sbin/nologin
 lp,/sbin/nologin
 sync,/bin/sync 
shutdown,/sbin/shutdown 
 halt,/sbin/halt
... 
[root@node2 ~]# awk 'BEGIN{printf "%15.2f\n",3.1415926}'
 3.14
 
6、awk输出重定向
print items > output-file
print items >> output-file
print items | command
 
特殊文件描述符:
/dev/stdin: 标准输入
/dev/stdout: 标准输出
/dev/stderr: 错误输出
 
7、awk的操作符
算术操作符:
x+y
x-y
x*y
x/y
x**y, x^y(x的y次方)
x%y
-x:负值
+x:转换为数值
字符串操作符:连接
赋值操作符:=,+=,-=,*=,/=,%=,^=,**=,++,--
(如果模式自身是=号,要写为/=/)
比较操作符:<,<=,>,>=,==,!=,~(模式匹配,左边的字符串能够被右边的模式所匹配为真,否则为假),!~
逻辑操作符:&&,||
 
■条件表达式:
selector?if-true-expression:if-false-expression
示例:
# awk -F: '{$3>=500?utype="common user":utype="admin or system user";print $1,"is",utype}' /etc/passwd
 
■函数调用:
function_name(argu1,argu2)
 
[root@node2 ~]# awk -F: '{$3>=500?utype="common user":utype="admin or system user";print $1,"is",utype}' /etc/passwd
...
tcpdump is admin or system user
wittgenstein is common user
mysql is admin or system user
dhcpd is admin or system user
apache is admin or system user
tesla is common user
 
8、控制语句
①if-else
格式:if (condition) {then body} else {else body}
# awk -F: '{if ($3>=500) {print $1,"is a common user"} else {print $1, "is an admin or system user"}}' /etc/passwd
# awk '{if (NF>=8) {print}}' /etc/inittab 
 
②while
格式:while (condition) {while body}
# awk '{i=1; while (i<=NF){printf "%s ",$i;i+=2};print ""}' /etc/inittab
# awk '{i=1; while (i<=NF){if (length($i)>=6) {print $i}; i++}}' /etc/inittab
length()函数:取字符串的长度
 
③do-while循环
格式:do {do-while body} while (condition)
 
④for循环
格式:for (variable assignment; condition; iteration process) {for body}
# awk '{for (i=1;i<=NF;i+=2){printf "%s ",$i};print ""}' /etc/inittab#显示每行的奇数字段
 注意:print会自动换行,而printf不会自动换行
# awk '{for (i=1;i<=NF;i++){if (length($i)>=6) {print $i}}}' /etc/inittab
 
for循环可用来遍历数组元素:
语法:for (i in array) {for body}
 
⑤case语句
格式:switch (expression) {case VALUE or /RGEEXP/: statement1;... default: stementN}
 
⑥循环控制
break
continue
 
⑦next:提前结束对本行的处理进而进入下一行的处理;
# awk -F: '{if($3%2==0) {next};print $1,$3}' /etc/passwd#显示UID为奇数的用户
# awk -F: '{if(NR%2==0) {next};print NR,$1}' /etc/passwd#显示奇数行的用户

[root@node2 ~]# awk '{if (NF>=8) {print}}' /etc/inittab #显示指定文件中字段数不少于8的行
# inittab is only used by upstart for the default runlevel.
# ADDING OTHER CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.
# Terminal gettys are handled by /etc/init/tty.conf and /etc/init/serial.conf,
# For information on how to write upstart event handlers, or how
# upstart works, see init(5), init(8), and initctl(8).
...
[root@node2 ~]# awk '{for (i=1;i<=NF;i+=2){printf "%s ",$i};print ""}' /etc/inittab
# is used upstart the runlevel. 
# 
# OTHER HERE HAVE EFFECT YOUR 
# 
# initialization started /etc/init/rcS.conf 
# 
# runlevels started /etc/init/rc.conf 
...
[root@node2 ~]# awk -F: '{if($3%2==0) {next};print $1,$3}' /etc/passwd
bin 1
adm 3
sync 5
halt 7
operator 11
gopher 13
nobody 99
dbus 81
...

 

9、数组
 array[index-expression]
 index-expression:可以使用任意字符串; 如果某数组元素事先不存在,那么在引用时,awk会自动创建此元素并将其初始化为空串;因此,要判断某数组是否存在某元素,必须使用“index in array”这种格式;
 A[first]="hello awk"
 print A[second]
 要遍历数组中的每一个元素,需要使用如下特殊结构:
for (var in array) {for body}
注意:var会遍历array的索引而非array元素值;
 删除数组元素:
 delete array[index]
 示例:
# netstat -tan | awk '/^tcp/{state[$NF]++}END{for (s in state) {print s,state[s]}}' #统计当前tcp连接中各种状态的个数
# awk '{ip[$1]++}END{for (i in ip) {print i,ip[i]}}' /var/log/httpd/access_log
 
[root@node2 ~]# netstat -tan | awk '/^tcp/{state[$NF]++}END{for (s in state) {print s,state[s]}}'
ESTABLISHED 3
LISTEN 14
 
10、awk的内置函数
①split(string,array[,fieldsep[,seps]]) 
功能:将string表示的字符串以fieldsep为分隔符进行切片,将切片后的结果保存至array为名的数组中;数组下标从1开始;
此函数有返回值,返回值为切片后的元素的个数
示例:
# awk 'BEGIN{split("root:x:0:0",user,":");print user[1]}'
# netstat -tn | awk '/^tcp/{lens=split($5,client,":");ip[client[lens-1]]++}END{for (i in ip) print i,ip[i]}'#显示tcp连接远程客户端的IP及其连接数
 
②length(string)
功能:返回给定字串的长度
 
③substr(string,start[,length])
功能:从string中取子串,从start为起始位置取length长度的子串;
 
[root@node2 ~]# awk 'BEGIN{split("root:x:0:0",user,":");print user[1]}'
root
[root@node2 ~]# netstat -tn | awk '/^tcp/{lens=split($5,client,":");ip[client[lens-1]]++}END{for (i in ip) print i,ip[i]}'
192.168.30.1 3
 
awk使用示例:
①小于GID不小于500的群组
 awk -F: '$3 >= 500{print $1}' /etc/group
②显示默认shell为nologin的用户
 awk -F: '$7~/nologin$/{print $1}' /etc/passwd
③显示eth0网卡配置文件的配置信息,只显示=号后的内容
 awk -F= '{pring $2}' /etc/sysconfig/network-scritps/eth0
④显示/etc/sysctl.conf文件定义的内核参数的参数名
 awk -F= '/^[^#]/{print $1}' /etc/sysctl.conf
⑤显示eth0网卡的ip地址
 ifconfig eth0 | awk -F: '/inet addr/{print $2}' | awk '{print $1}'

相关内容