awk和流编辑器sed在工作原理和用法上有很多类似之处,它们都是检查输入数据中的行是否匹配指定的模式,如果匹配成功就对匹配的行执行相应的操作,重复这个过程指导所有的输入数据都被处理完,因此awk和sed都是面向数据流的工具。此外,awk和sed的相似之处还体现在两者使用的语法式很类似,都是使用正则表达式匹配来确定要操作的行。与sed不同的是,awk执行的操作要写在模式后面的花括号{}中。
案例1:显示出系统中某个特定用户(如root用户)的用户名、用户ID以及主目录。
#提取passwd文件中某一行的三个字段
$awk –F: ‘/^root:/{PRint $1,$3,$6;}’/etc/passwd
root 0 /root
解析:/etc/passwd文件的各个字段是由冒号隔开,而awk的默认分隔符是空格,所以通过命令行选项-F:来指定;$1,$3,$6是指第一、三、六字段;
案例2:输出系统中所有用户的用户名及相应的用户ID的表格,并在表格的第一行为这两列数据添加标题。
具体代码实现如下[user.awk]:
#在主循环开始以前打印标题,使下面的数据的含义更加明确
print title to clarify the following databefore main loop begin
BEGIN {
printf "%-15s%s/n","USERNAME","UID";
printf"--------------/n";
}
#主循环开始运行
#从/etc/passwd文件中提取用户名和UID
{
printf "%-15s%s/n",$1,$3;
}
#通过选项-f指定要运行的awk程序
$awk –F: -f user.awk /etc/passwd
案例3:如何显示出当前系统中所有用户ID大于等于1000的用户?
#使用比较表达式,而不是模式匹配
$awk –F: ‘$3>=1000{ printf “User:%-15sUID:%s/n”,$1,$3;}’ /etc/passwd
格式:
expression { actions; }
当有多高表达式时,使用逻辑与&&和逻辑或||把多个表达式连接在一起构成更复杂的表达式,如搜索当前系统中所有使用bash和C Shell的用户:
$awk –F: ‘($7==”/bin/bash”)|| ($7==”/bin/csh”) { printf “User:%-15sUID:%-15sShell:%s/n”,$1,$3,$7; }’/etc/passwd
案例4:
awk 程序中的变量和Shell脚本中的变量很类似,都是用来存储一个值的。通过在awk程序中定义变量,我们可以更好地控制awk如何处理数据。处理用户自定义的变量以外,awk还预定义了一些内建变量,通过这些变量我们可以得到很多有用的信息。
总结awk常用的内建变量:
内建变量 | 描述 |
FILENAME | awk当前读取数据的文件,如果从标准输入中读取数据,变量的值为横杠-,在BEGIN规则中变量为空 |
FNR | 当前文件中的当前行的行号,每读一行数据,它读值就会加1,在读取一个新文件时会被设置为0 |
NR | 所处理的所有记录的个数。每一个新行被读,它的值就会自动加1 |
FS | 输入行的字符分隔符,默认值为空格,可以通过命令行选项-F指定 |
NF | 当前记录中所包含的字段个数,每次读入一个新的记录,它的值都会被重现设置 |
OFS | 输出时的字段分隔符,默认是一个空格 |
ORS | 输出时的记录分隔符,默认是换行符 |
RS | 输入时的记录分隔符,默认是换行符 |
案例2:写一个脚本来统计文件中的总行数,空白行的行数及注释行的行数。
#!/bin/bash#本脚本需要至少一个命令行参数if [ "$#" -eq 0 ]then echo "Usage;" echo -e "/t'basename $0' [ FILE ]...." exit 1fi #处理每一个命令行参数for FILE in "$@";do #只有当文件是一个可读的文件,并且是一个含有数据的普通文件时才能被本脚本处理 if [ -f "$FILE" ] && [ -r "$FILE" ] && [ -s "$FILE" ] then echo "$FILE:" awk ' #统计所有的行数 /.*/{ total_lines += 1; } #正则表达式/.*/匹配所有行,其中点代表一个任意的字符,而星号表示0个或任意个的星号前面的字符 #统计所有的空行数 /^ *$/{ empty_lines += 1; next; } #正则表达式/^ *$/表示开头任意个空格,以$结尾 #统计所有的注释行行数 /^ *#.*$/{ comment_lines += 1; } #正则表达式/^ *#.*$/表示#前可能有空格,最后以$结尾 #在主循环结束后输出结果 END { printf "/tTotal Lines:%s/t/t",total_lines; printf "Empty Lines:%s/t/t",empty_line; printf "Comment Lines:%s/n",comment_lines; } ' "$FILE" else echo "$FILE can not be handled!" fidoneecho "Done."exit 0
案例3:如何打印/etc/passwd文件中的偶数行?
#!/bin/bashawk ' BEGIN { #在主循环开始以前制定分隔符为冒号 FS=":"; } #通过表达式匹配所有的偶数行,awk内建变量NR来实现 NR%2 == 0 { print NR,$0; }' /etc/passwdexit 0
案例4:如何处理多行记录中提取开发者的姓名和E-mail地址?
#!/bin/bashawk ' BEGIN { # 设置分隔符为换行符/n FS="/n"; #设置输入记录分隔符为空字符串 RS=""; #设置输出字段分隔符为换行符 OFS="/N" #设置输出记录分隔符为两个换行符 ORS="/n/n" #打印标题 print "The developers of linux Kernel:" print "(N)name,(E)email" print "---------------" } #使用正则表达式^N过滤所有无关数据,只对以字母N起始的记录块进行处理 /^N/ { print $1,$2; }' CREDITSexit 0
案例5:在上一例子中,我们提取出开发者的姓名和E-mail地址,如果希望得到每一个开发者的姓,也即名字字符串中的最后一个字段,应该怎么做呢?
#!/bin/bashawk ' BEGIN { # 设置分隔符为换行符/n FS="/n"; #设置输入记录分隔符为空字符串 RS=""; #设置输出字段分隔符为换行符 OFS="/N" #设置输出记录分隔符为两个换行符 ORS="/n/n" #打印标题 print "The developers of Linux Kernel:" print "(N)name,(E)email" print "---------------" } #使用正则表达式^N过滤所有无关数据,只对以字母N起始的记录块进行处理 /^N/ { #输出以字母N起始的开发者的全名 print $1; }#第一个awk程序的处理结果会继续发送第二个awk程序进行处理' CREDITS|awk ' BEGIN { print "The Last Name of Developers:" print "--------------------" } /^N/{ #打印行号和最后一个字段,NF表示字段个数,$NF表示最后一个字段 print NR,$NF; }'exit 0
案例6:awk不能直接读取到shell变量,可通过命令行创建awk变量的方式把shell变量的值传递到awk程序中。
格式:
#使用环节变量定义awk变量
awk ‘script’ variable1=VALUE1variable2=VALUE2…files…
在awk程序中还可以使用控制语句,包括if语句、while语句、for语句和do-while语句。
awk中的函数分内建函数和自定义函数。
新闻热点
疑难解答