Linux_Shell知多少—3—awk应用

AWK 用法

相较于 sed 常常作用于一整个行的处理,awk则比较倾向于一行当中分成数个『字段』来处理
它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具
awk的语法有两种形式:
一、[root@www ~]# awk '条件类型1{动作1} 条件类型2{动作2} ...' filename
二、[root@www ~]# awk -f awkscriptfile input_file
-f :选项指明在文件awkscriptfile中的awk脚本, input_file( s )是使用awk进行浏览的文件名

awk 后面接两个单引号并加上大括号{}来设定想要对数据进行的处理动作
awk 可以处理后续接的档案,也可以读取来自前个指令的standard output
awk 主要是处理『每一行的字段内的数据』

awk 默认的『字段的分隔符为"空格键"或"[tab]键"』
awk [-F '指定域分隔符'] 'commands' input.file
[-F域分隔符]是可选的,因为awk使用空格作为缺省的域分隔符
awk 最常使用的动作!透过print的功能将字段数据列出来!

awk:模式和动作
任何awk语句都由模式和动作组成
模式部分决定动作语句何时触发及触发事件。处理即对数据进行的操作。
如果省略模式部分,动作将时刻保持执行状态

模式可以是任何条件语句或复合语句或正则表达式。
模式包括两个特殊字段BEGIN和END
使用BEGIN语句设置计数和打印头
BEGIN语句使用在任何文本浏览动作之前,之后文本浏览动作依据输入文件开始执行
END语句用来在awk完成文本浏览动作后打印输出文本总数和结尾状态标志
如果不特别指明模式,awk总是匹配或打印行数

实际动作在大括号{ }内指明
动作大多数用来打印,但是还有些更长的代码诸如if和循环(looping)语句及循环退出结构

域和记录
awk执行时,其浏览域标记为$1,$2 ... $n。这种方法称为域标识
使用$1,$3表示参照第1和第3域,注意这里用逗号做域分隔
如果希望打印一个有5个域的记录的所有域,不必指明$1,$2,$3,$4,$5 可使用$0,意即所有域

因为awk默认是输出$0的,所以一般不需要加上: print $0
保存awk输出
[root@www shell]# cat grade.txt
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
P.Bunny 02/99   48      Yellow          12      35      28
J.Troll 07/99   4842    Brown-3         12      26      26
L.Tansl 05/99   4712    Brown-2         12      30      28
重定向: >   <==不在屏幕上输出,注:在确保正确的情况下使用
[root@www shell]# awk '{print $0}' grade.txt > wow
[root@www shell]# cat wow
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
P.Bunny 02/99   48      Yellow          12      35      28
J.Troll 07/99   4842    Brown-3         12      26      26
L.Tansl 05/99   4712    Brown-2         12      30      28
tee命令:   <==保存同时,还输出到屏幕上
[root@www shell]# awk '{print $0}' grade.txt | tee delete_me_and_die
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
P.Bunny 02/99   48      Yellow          12      35      28
J.Troll 07/99   4842    Brown-3         12      26      26
L.Tansl 05/99   4712    Brown-2         12      30      28
[root@www shell]# cat delete_me_and_die
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
P.Bunny 02/99   48      Yellow          12      35      28
J.Troll 07/99   4842    Brown-3         12      26      26
L.Tansl 05/99   4712    Brown-2         12      30      28

使用标准输入
正常方法:
belts.awk grade_student.txt
使用重定向方法:
belts.awk < grade2.txt
或管道方法:
grade2.txt | belts.awk

打印所有记录    ==$0==
[root@www shell]# awk '{print $0}' grade.txt
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
P.Bunny 02/99   48      Yellow          12      35      28
J.Troll 07/99   4842    Brown-3         12      26      26
L.Tansl 05/99   4712    Brown-2         12      30      28
打印单独记录
[root@www shell]# awk '{print $1 "/t" $3}' grade.txt
M.Tans  48311
J.Lulu  48317
P.Bunny 48
J.Troll 4842
L.Tansl 4712
打印报告头    <==输出文本加入信息头 BEGIN 语句应用
[root@www shell]# awk  'BEGIN {print "Name Belt/n------------"} {print $1 "/t" $4}' grade.txt
Name Belt
------------
M.Tans  Green
J.Lulu  green
P.Bunny Yellow
J.Troll Brown-3
L.Tansl Brown-2
打印信息尾        <==END语句应用
[root@www shell]# awk  'BEGIN {print "Name/n---"} {print $1} END {print "---/nend-of-report"}' grade.txt
Name
---
M.Tans
J.Lulu
P.Bunny
J.Troll
L.Tansl
---
end-of-report

awk中正则表达式及其操作
特殊的两个,其它基本相同    只适用于awk而不适用于grep或sed
+     使用+匹配一个或多个字符
?     匹配模式出现频率。例如使用/X Y?Z/匹配X Y Z或Y Z

awk条件操作符
操作符    描述
<             小于
>=             大于等于
<=             小于等于
~             匹配正则表达式
==             等于
!~             不匹配正则表达式
!=             不等于

1. 匹配  重点了解    if ($4~/Brown/)
为使一域号匹配正则表达式,使用符号‘~’后紧跟正则表达式。也可用if语句,awk中if后面的条件用()括起来
[root@www shell]# awk '{if ($4~/Brown/) print $0}' grade.txt
J.Troll 07/99   4842    Brown-3         12      26      26
L.Tansl 05/99   4712    Brown-2         12      30      28
匹配记录找到时,如果不特别声明,awk缺省打印整条记录
[root@www shell]# awk '$0~/Brown/' grade.txt
J.Troll 07/99   4842    Brown-3         12      26      26
L.Tansl 05/99   4712    Brown-2         12      30      28
2. 精确匹配  '$3 == "48"
[root@www shell]# awk '$3 == "48" {print $0}' grade.txt
P.Bunny 02/99   48      Yellow          12      35      28
3. 不匹配
[root@www shell]# awk '{if ($3 ~! /48/)print $0}' grade.txt
L.Tansl 05/99   4712    Brown-2         12      30      28
[root@www shell]# awk '$3 !~ /48/' grade.txt
L.Tansl 05/99   4712    Brown-2         12      30      28
[root@www shell]# awk '$4 != "Brown-2" {print $0}' grade.txt
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
P.Bunny 02/99   48      Yellow          12      35      28
J.Troll 07/99   4842    Brown-3         12      26      26
4. 小于
[root@www shell]# awk  '{if ($6 <  $7) print $0}' grade.txt
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
[root@www shell]# awk '$6 < $7 {print $0}' grade.txt
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
5. 小于等于
[root@www shell]# awk  '{if ($6 <=  $7) print $0}' grade.txt
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
J.Troll 07/99   4842    Brown-3         12      26      26
[root@www shell]# awk '$6 <= $7 {print $0}' grade.txt
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
J.Troll 07/99   4842    Brown-3         12      26      26
6. 大于
[root@www shell]# awk  '{if ($6 >  $7) print $0}' grade.txt
P.Bunny 02/99   48      Yellow          12      35      28
L.Tansl 05/99   4712    Brown-2         12      30      28
[root@www shell]# awk '$6 > $7 {print $0}' grade.txt
P.Bunny 02/99   48      Yellow          12      35      28
L.Tansl 05/99   4712    Brown-2         12      30      28
7. 设置大小写
为查询大小写信息,可使用[ ]符号
[root@www shell]# awk '{if ($4 ~ /[Gg]reen/) print $0}' grade.txt
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
[root@www shell]# awk '$4 ~ /[Gg]reen/' grade.txt
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
8. 任意字符
[root@www shell]# awk '{if ($1 ~ /^...a/) print $0}' grade.txt
M.Tans  5/99    48311   Green           8       40      44
L.Tansl 05/99   4712    Brown-2         12      30      28
[root@www shell]# awk '$1 ~ /^...a/' grade.txt
M.Tans  5/99    48311   Green           8       40      44
L.Tansl 05/99   4712    Brown-2         12      30      28
9. 或关系匹配
[root@www shell]# awk '$0 ~ /Brown|Yellow/' grade.txt
P.Bunny 02/99   48      Yellow          12      35      28
J.Troll 07/99   4842    Brown-3         12      26      26
L.Tansl 05/99   4712    Brown-2         12      30      28
[root@www shell]# awk '{if ($0 ~ /Brown|Yellow/) print $0}' grade.txt
P.Bunny 02/99   48      Yellow          12      35      28
J.Troll 07/99   4842    Brown-3         12      26      26
L.Tansl 05/99   4712    Brown-2         12      30      28
10. 行首
[root@www shell]# awk '{if (/^M/) print $0}' grade.txt
M.Tans  5/99    48311   Green           8       40      44
[root@www shell]# awk '/^M/' grade.txt
M.Tans  5/99    48311   Green           8       40      44
复合模式或复合操作符用于形成复杂的逻辑操作
&& AND     : 语句两边必须同时匹配为真
|| O R    :语句两边同时或其中一边匹配为真
!             :非求逆
11. AND
&& 意为&&两边匹配均为真
[root@www shell]# awk '{if ($1 == "P.Bunny" && $4 == "Yellow") print $0}' grade.txt
P.Bunny 02/99   48      Yellow          12      35      28
|| 符号两边的匹配模式之一或全部为真
[root@www shell]# awk '{if ($4 == "Yellow" || $4 ~ /Brown/) print $0}' grade.txt
P.Bunny 02/99   48      Yellow          12      35      28
J.Troll 07/99   4842    Brown-3         12      26      26
L.Tansl 05/99   4712    Brown-2         12      30      28

awk内置变量
awk有许多内置变量用来设置环境信息。这些变量可以被改变
========================
ARGC             命令行参数个数
ARGV            命令行参数排列
ENVIRON     支持队列中系统环境变量的使用
FILENAME     awk 浏览的文件名
FNR             浏览文件的记录数
FS                设置输入域分隔符,等价于命令行- F选项
NF                浏览记录的域的个数,既有多少个域
NR                已读的记录数
OFS             输出域分隔符
ORS             输出记录分隔符
RS                控制记录分隔符
-----
ARGC            支持命令行中传入awk脚本的参数个数。
ARGV            是ARGC的参数排列数组,其中每一元素表示为ARGV[n],n为期望访问的命令行参数
ENVIRON     支持系统设置的环境变量,要访问单独变量,使用实际变量名 例如  ENVIRON [“EDITOR”]=“Vi”
FILENAME    支持awk脚本实际操作的输入文件
FNR                支持awk目前操作的记录数。其变量值小于等于NR
FS                用来在a w k中设置域分隔符,与命令行中- F选项功能相同
NF                支持记录域个数,在记录被读之后再设置
OFS                允许指定输出域分隔符,缺省为空格。如果想设置为#,写入OFS="#"
ORS                为输出记录分隔符,缺省为新行(/n)
RS                是记录分隔符,缺省为新行(/n)

NF、NR和FILENAME
要快速查看记录个数,应使用NR
使用NR将打印输入文件的记录个数。print NR 放在END语法中
[root@www shell]# awk 'END {print NR}' grade.txt
5
所有人记录被打印,并带有其记录号
使用NF变量显示每一条读记录中有多少个域
并在END部分打印输入文件名
[root@www shell]# awk '{print NF,NR,$0} END {print FILENAME}' grade.txt
7 1 M.Tans      5/99    48311   Green           8       40      44
7 2 J.Lulu      06/99   48317   green           9       24      26

只有在文件中至少有一个记录时才查询Brown级别记录,使用AND复合语句实现这一功能
[root@www shell]# awk '{if (NR > 0 && $4 ~ /Brown/) print $0}' grade.txt
J.Troll 07/99   4842    Brown-3         12      26      26
L.Tansl 05/99   4712    Brown-2         12      30      28
[root@www shell]# awk 'NR > 0 && $4 ~ /Brown/ {print $0}' grade.txt
J.Troll 07/99   4842    Brown-3         12      26      26
L.Tansl 05/99   4712    Brown-2         12      30      28

NF的一个强大功能是将变量$PWD的返回值传入awk并显示其目录。这里需要指定域分隔符/。
[root@www shell]# echo $PWD | awk -F'/' '{print $NF}'
shell
显示文件名
[root@www shell]# echo "/etc/sysconfig/iptables" | awk -F'/' '{print $NF}'
iptables

awk中,$0变量表示整个当前行,所以print和print $0的作用完全一样
[root@www shell]# awk '{ print }' /etc/passwd
[root@www shell]# awk '{ print $0 }' /etc/passwd
在awk内的 NR, NF 等变量要用大写,且不需要有字符样$
取行:$0 <== 指整行
[root@localhost local]# last -n 5 | awk  '{print $0}'
NR <==表示行,取第几行
[root@localhost local]# last -n 5 | awk  'NR == 1 {print $0}'
$1,$2,$3....  <==第几域
打印第一列和第三列
[root@localhost local]# awk '{print $1 "/t" $3}'
打印第一行第一列
[root@localhost local]# last -n 5 | awk  'NR == 1 {print $0}' | awk '{print $1}'
多字段输出:
[root@www shell]# awk -F":" '{ print "username: "$1,"/t uid: "$3 }' passwd

awk操作符 基本表达式可以划分为数字型、字符串型、变量型、域及数组元素
= += *= / = %= ^ =         赋值操作符
?                                         条件表达操作符
|| && !                             并、与、非(上一节已讲到)
~ !~                                     匹配操作符,包括匹配和不匹配
< <= == != >>                 关系操作符
+ - * / % ^                     算术操作符
+ + --                                 前缀和后缀

1. 设置输入域到域变量名
[root@www shell]# awk '{name=$1;belts=$4;if (belts ~ /Yellow/) print name " is belt " belts }' grade.txt
P.Bunny is belt Yellow
注意:变量用;分开
2. 域值比较操作
有两种方式测试一数值域是否小于另一数值域。
1) 在BEGIN中给变量名赋值。
2) 在关系操作中使用实际数值。
通常在BEGIN部分赋值是很有益的,可以在awk表达式进行改动时减少很多麻烦。
使用关系操作必须用圆括号括起来。
[root@www shell]# awk '{if ($6 < $7) print $0}' grade.txt
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
[root@www shell]# awk '{liu=$6;qi=$7;if (liu < qi) print liu "  < " qi }' grade.txt
40  < 44
24  < 26 
给数字赋以变量名BASELINE和在BEGIN部分给变量赋值
[root@www shell]# awk 'BEGIN{BASELINE="27"} {if ($6 < BASELINE) print $0}' grade.txt
J.Lulu  06/99   48317   green           9       24      26
J.Troll 07/99   4842    Brown-3         12      26      26
3. 修改数值域取值
当在awk中修改任何域时,重要的一点是要记住实际输入文件是不可修改的,修改的只是保存在缓存里的awk复本
awk会在变量NR或NF变量中反映出修改痕迹。
修改数值域,简单的给域标识重赋新值,如:$1=$1+5,会将域1数值加5,但要确保赋值域其子集为数值型。
修改M.Tans的目前级别分域,使其数值从40减为39,使用赋值语句$6=$6-1
[root@www shell]# awk '{if ($1 ~ /M.Tans/) $6=$6-1;print $0}' grade.txt
M.Tans 5/99 48311 Green 8 39 44
J.Lulu  06/99   48317   green           9       24      26
输出有误;要注意的地方,正确的如下:
[root@www shell]# awk '{if ($1 == "M.Tans") {$6=$6-1;print $0} }' grade.txt
M.Tans 5/99 48311 Green 8 39 44
4. 修改文本域
修改文本域即对其重新赋值。需要做的就是赋给一个新的字符串。
在J.Troll中加入字母,使其成为J.L.Troll,表达式为$1="J.L.Troll",记住字符串要使用双秒号(""),并用圆括号括起整个语法。
[root@www shell]# awk '{if ($1 ~ /J.Troll/) {$1="J.L.Troll";print $1}}' grade.txt
J.L.Troll
6. 创建新的输出域
在awk中处理数据时,基于各域进行计算时创建新域是一种好习惯
创建新域要通过其他域赋予新域标识符
创建一个基于其他域的加法新域{$4=$2+$3},这里假定记录包含3个域,则域4为新建域,保存域2和域3相加结果
[root@www shell]#awk 'BEGIN{print "Name/tnewyu"} {if ($6<$7) {$8=$7-$6;print $1"/t"$8}}' grade.txt
Name    newyu
M.Tans  4
J.Lulu  2
以变量名的方式
awk 'BEGIN{print "Name/tnewyu"} {if ($6<$7) {diff=$7-$6;print $1"/t"diff}}' grade.txt
Name    newyu
M.Tans  4
J.Lulu  2
7. 增加列值  <==域值求和
为增加列数或进行运行结果统计,使用符号+=
增加的结果赋给符号左边变量值,增加到变量的域在符号右边。
例如将$1加入变量total,表达式为total += $1 列值增加很有用
awk浏览的整个文件的域6结果总和
[root@www shell]# awk '(total+=$6);END{print "Club student total points: " total}' grade.txt
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
P.Bunny 02/99   48      Yellow          12      35      28
J.Troll 07/99   4842    Brown-3         12      26      26
L.Tansl 05/99   4712    Brown-2         12      30      28
Club student total points: 155
[root@www shell]# awk '{(total+=$6)};END{print "Club student total points: " total}' grade.txt
Club student total points: 155
8. 文件长度相加
在目录中查看文件时,如果想快速查看所有文件的长度及其总和
但要排除子目录,使用ls -l命令,然后管道输出到awk
awk首先剔除首字符为d(使用正则表达式)的记录,然后将文件长度列相加,并输出每一文件长度及在END部分输出所有文件的长度
首先用ls -l命令查看一下文件属性
注意第二个文件属性首字符为d,说明它是一个目录,文件长度是第5列,文件名是第8列。如果系统不是这样排列文件名及其长度,应适时加以改变
 ls -l | awk '/^[^d]/ {print $8"/t/t"$5} {(total+=$5)} END {print "total KB:" total}'
total kb: 4979

内置的字符串函数
gsub(r,s)                         在整个$0中用s替代r
gsub(r,s,t)                     在整个t中用s替代r
index(s,t)                         返回s中字符串t的第一位置
length(s)                         返回s长度
match(s,r)                         测试s是否包含匹配r的字符串
split(s,a,fs)                 在fs上将s分成序列a
sprint(fmt,exp)             返回经fmt格式化后的exp
sub(r,s)                             用$0中最左边最长的子串代替s
substr(s,p)                     返回字符串s中从p开始的后缀部分
substr(s,p,n)                 返回字符串s中从p开始长度为n的后缀部分

gsub函数                有点类似于sed查找和替换。
                                它允许替换一个字符串或字符为另一个字符串或字符,并以正则表达式的形式执行。
                                第一个函数作用于记录$0,第二个gsub函数允许指定目标,然而,如果未指定目标,缺省为$0
index函数                返回目标字符串s中查询字符串t的首位置。
length函数            返回字符串s字符长度   
match函数                测试字符串s是否包含一个正则表达式r定义的匹配
split                        使用域分隔符fs将字符串s划分为指定序列a   
sprint函数            类似于printf函数(以后涉及),返回基本输出格式fmt的结果字符串exp
sub(r,s)函数        将用s替代$0中最左边最长的子串,该子串被(r)匹配
sub(s,p)函数        返回字符串s在位置p后的后缀。
substr(s,p,n)        同上,并指定子串长度为n       

awk基本函数调用公式:
[root@www shell]# awk 'hanshu {print $0}' grade.txt
1. gsub
要在整个记录中替换一个字符串为另一个,使用正则表达式格式, /目标模式/,替换模式/
<==48311转换为49999
[root@www shell]# awk 'gsub(/4842/,4899)' grade.txt
J.Troll 07/99   4899    Brown-3         12      26      26
[root@www shell]# awk  'gsub(/4842/,4899) {print $0}' grade.txt
J.Troll 07/99   4899    Brown-3         12      26      26
2. index
查询字符串s中t出现的第一位置。必须用双引号将字符串括起来。
例如返回目标字符串Bunny中ny出现的第一位置,即字符个数
[root@www shell]# awk 'BEGIN {print index("Bunny","ny")}' grade.txt
4
3. length
返回所需字符串长度,例如检验字符串J.Troll返回名字及其长度
[root@www shell]# awk '$1=="J.Troll" {print length($1) "/t" $1}' grade.txt
7       J.Troll
还有一种方法,这里字符串加双引号。
[root@www shell]# awk 'BEGIN {print length("ni hao")}'
6
4. match
match 测试目标字符串是否包含查找字符的一部分
            可以对查找部分使用正则表达式,返回值为成功出现的字符排列数
            <==如果未找到,返回0
在ANCD中查找d。因其不存在,所以返回0
[root@www shell]# awk 'BEGIN {print match("ANCD",/d/)}'
0
在ANCD中查找D。因其存在,所以返回ANCD中D出现的首位置字符数
[root@www shell]# awk 'BEGIN {print match("ANCD",/D/)}'
4
在学生J.Lulu中查找u
[root@www shell]# awk '$1 == "J.Lulu" {print match($1,"u")}' grade.txt
4
5. split
使用split返回字符串数组元素个数。
使用split,指定分隔符及数组名。
[root@www shell]# awk 'BEGIN {print split("123-456-789",pats_array,"-")}'
3
使用不同的分隔符
[root@www shell]# awk 'BEGIN {print split("123#456#789",myarray,"#")}'
3
6. sub
使用sub发现并替换模式的第一次出现位置。
[root@www shell]# awk '$1 == "J.Troll" sub(/26/,"29",$0)' grade.txt
7. substr
substr是一个很有用的函数。它按照起始位置及长度返回字符串的一部分
[root@www shell]# awk '$1=="L.Tansl" {print substr($1,1,3)}' grade.txt
L.T
[root@www shell]# awk '$1=="L.Tansl" {print substr($1,3,7)}' grade.txt
Tansl
从文本文件中抽取姓氏,需操作域1,并从第三个字符开始:
[root@www shell]# awk '{print substr($1,3)}' grade.txt
Tans
Lulu
Bunny
Troll
Tansl
在BEGIN部分定义字符串,在END部分返回从第t个字符开始抽取的子串
[root@www shell]# awk 'BEGIN {STR="A FEW GOOD MEN"} END{print substr(STR,7)}' grade.txt
GOOD MEN

8. 从shell中向awk传入字符串
使用管道将字符串Hello World传入awk,返回其长度
[root@www shell]# echo "Hello World" | awk '{print length($0)}'
11
设置文件名为一变量,管道输出到awk,返回不带扩展名的文件名
[root@www shell]# str1=mydoc.txt
[root@www shell]# echo $str1 | awk '{print substr($str1,1,5)}'
mydoc
设置文件名为一变量,管道输出到awk,只返回其扩展名
[root@www shell]# str1=mydoc.txt
[root@www shell]# echo $str1 | awk '{print substr($str1,7)}'
txt

字符串屏蔽序列
使用字符串或正则表达式时,有时需要在输出中加入一新行或查询一元字符
打印一新行时,(新行为字符/n),给出其屏蔽序列,以不失其特殊含义,用法为在字符串前加入反斜线。例如使用/n强迫打印一新行。
列出awk识别的另外一些屏蔽序列
/b             退格键
/t             tab键
/f             走纸换页
/ddd         八进制值
/n             新行
/c             任意其他特殊字符,例如/ /为反斜线符号
/r             回车键

[root@www shell]# awk 'BEGIN{print "/nMay/tDay/n/nMay/tDay"}'

May     Day

May     Day

awk输出函数printf
awk提供函数printf,拥有几种不同的格式化输出功能。例如按列输出、左对齐或右对齐方式。
每一种printf函数(格式控制字符)都以一个%符号开始,以一个决定转换的字符结束。转换包含三种修饰符。
printf函数基本语法是: printf([格式控制符],参数),格式控制字符通常在引号里

awk printf修饰符
-                     左对齐
Width             域的步长,用0表示0步长
.prec                最大字符串长度,或小数点右边的位数
--------------
awk printf格式
%c                     ASCII字符
%d                     整数
%e                     浮点数,科学记数法
%f                     浮点数,例如(123.44)
%g                     awk决定使用哪种浮点数转换e或者f
%o                     八进制数
%s                     字符串
%x                     十六进制数

1. 字符转换
查到ASCII码中65的等价值。管道输出65到awk。 printf进行ASCII码字符转换
[root@www shell]# echo "65" | awk '{printf "%c/n",$0}'
A
[root@www shell]# awk 'BEGIN {printf "%c/n",65 }'
A
浮点数转换后‘999’的输出结果。整数传入后被加了六个小数点。
[root@www shell]# awk 'BEGIN{printf "%f/n",999}'
999.000000
2. 格式化输出
打印所有的学生名字和序列号,要求名字左对齐, 15个字符长度,后跟序列号。注意/ n换行符放在最后一个指示符后面。输出将自动分成两列
[root@www shell]# awk '{printf "%-15s %s/n",$1,$3}' grade.txt
M.Tans          48311
J.Lulu          48317
P.Bunny         48
J.Troll         4842
L.Tansl         4712
注意这里使用print加入头信息
[root@www shell]# awk 'BEGIN {print "Name/t/tS.Number"} {printf "%-15s %s/n",$1,$3}' grade.txt
Name            S.Number
M.Tans          48311
J.Lulu          48317
P.Bunny         48
J.Troll         4842
L.Tansl         4712
3.向一行awk命令传值
在查看awk脚本前,先来查看怎样在awk命令行中传递变量。
在awk执行前将值传入awk变量,需要将变量放在命令行中,格式如下:
awk 命令变量=输入文件值
命令行中设置变量AGE等于10,然后传入awk中,查询年龄在10岁以下的所有学生
[root@www shell]# awk '{if ($5 < AGE) print $0}' AGE=10 grade.txt
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
要快速查看文件系统空间容量,观察其是否达到一定水平,可使用下面awk一行脚本
df -Th | awk '$NF == "/"{ print $0}' | awk '{if ($4<TAG) print $6"/t"$4 }' TAG=200
/       190G
加入输入信息头
df -Th | awk '$NF == "/"{ print $0}' | awk 'BEGIN {print "Systemfile/tFreeDisk"}{if ($4<TAG) print $6"/t/t"$4 }' TAG=200
Systemfile      FreeDisk
/               190G
awk也允许传入环境变量。下面的例子使用环境变量HOME支持当前用户目录。
[root@www ~]# pwd | awk '{if ($1==derr) print $1}' derr=$HOME
/root
4. awk脚本文件
可以将awk脚本写入一个文件再执行它。
[root@www shell]# cat student_tot.awk
#!/bin/awk -f
BEGIN{
print "Student  Date  Member No.  Grade Age  Points     Max"
print "Name     Joined                        Gained    Point Available"
print "=============================================================="
}

(total+=$6)

END{
print "Club student total points :" total
print "Average Club Student Points:" total/NR
print NR}
/bin/awk -f。这很重要,没有它自包含脚本将不能执行。这一行告之脚本系统中awk的位置
通过将命令分开,脚本可读性提高,还可以在命令之间加入注释
这里加入头信息和结尾的平均值。基本上这是一个一行脚本文件。
执行结果:
[root@www shell]# ./student_tot.awk grade.txt
Student  Date  Member No.  Grade Age  Points    Max
Name     Joined                        Gained   Point Available
==============================================================
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
P.Bunny 02/99   48      Yellow          12      35      28
J.Troll 07/99   4842    Brown-3         12      26      26
L.Tansl 05/99   4712    Brown-2         12      30      28
Club student total points :155
Average Club Student Points:31
5
现在用awk脚本过滤出错误行的出现频率,使得每一个失败记录只对应一个错误行
[root@www shell]# cat strip
etreiytrpytyu
ERROR*
ERROR*
ERROR*
ERROR*
IUEWROPYJRTMELUYK
ERROR*
ERROR*
ERROR*
ERROR*
ERROR*
ERROR*
EWUTIRWJYHT
[root@www shell]# vi error_strip.awk
#!/bin/awk -f

BEGIN {error_line=""}
{if ($0=="ERROR*" && error_line=="ERROR*")
next;
error_line=$0;print}
[root@www shell]# ./error_strip.awk strip
etreiytrpytyu
ERROR*
IUEWROPYJRTMELUYK
ERROR*
EWUTIRWJYHT
5. 在awk中使用FS变量
如果使用非空格符做域分隔符(FS)浏览文件
例如# 或:编写这样的一行命令很容易,因为使用FS选项可以在命令行中指定域分隔符。
[root@www shell]# cat passwd.awk
#!/bin/awk -f

BEGIN {
FS=":"
}
{
print $1 "/t/t" $7
}
[root@www shell]# ./passwd.awk passwd
root            /bin/bash
bin             /sbin/nologin

6. 向awk脚本传值
向awk脚本传值与向awk一行命令传值方式大体相同,格式为:
awk script_file var=value input_file
检查文件中域号和指定数字。
这里使用了NF变量MAX,表示指定检查的域号,使用双引号将域分隔符括起来,即使它是一个空格。
[root@www shell]# cat fieldcheck.awk
#!/bin/awk -f

NF!=MAX{
print("line" NR " does not have " MAX "fields")}
如果NF中的值不等于最大MAX值,则打印出"哪一行的域总数不是MAX"
[root@www shell]# ./fieldcheck.awk MAX=7 FS=":" passwd
[root@www shell]# cat name.awk
#!/bin/awk -f

{
if ($5<AGE)
print $0
}
[root@www shell]# ./name.awk AGE=10 grade.txt
M.Tans  5/99    48311   Green           8       40      44
J.Lulu  06/99   48317   green           9       24      26
同样可以使用前面提到的管道命令传值,下述awk脚本从du命令获得输入,并输出块和字节数
[root@www shell]# cat duawk.awk
#!/bin/awk -f
BEGIN{
OFS="/t";
print "name" "/t/t","bytes" "/t" "blocks"
print "==============================="}
{print $2,"/t/t",$1*512,$1}
[root@www shell]# du * | ./duawk.awk
name                    bytes   blocks
===============================
02.sh                           2048    4
awk                             0       0
catfile                         2048    4

数组
split函数时,提到怎样使用它将元素划分进一个数组
[root@www shell]# awk 'BEGIN {print split("123#456#789",myarray,"#")}'
3
数组使用前,不必定义,也不必指定数组元素个数。
经常使用循环来访问数组。
下面是一种循环类型的基本结构:
For (element in array ) print array[element]
对于记录123#456#789 先使用split函数划分它,再使用循环打印各数组元素
[root@www shell]# cat arraytest.awk
#!/bin/awk -f
BEGIN{
record="123#456#789";
split(record,myarray,"#")}
END{for (i in myarray) {print myarray[i]}}
要运行脚本,使用/dev/null作为输入文件
[root@www shell]# ./arraytest.awk /dev/null
123
456
789
数组和记录
学生级别及是否是成人或未成年人的信息,有两个域,分隔符为(#)
[root@www shell]# cat grade_student.txt
Yellow#Junior
Orange#Senior
Yellor#Junior
Purple#Junior
Brown-2#Junior
White#Senior
Orange#Senior
Red#Junior
Red#Junior
Brown-2#Senior
Yellow#Senior
Red#Junior
Blue#Senior
Green#Senior
Purple#Junior
White#Junior
脚本功能是读文件并输出下列信息。
1) 俱乐部中Yellow、Orange和Red级别的人各是多少
2 ) 俱乐部中有多少成年人和未成年人
[root@www shell]# cat grade_student.txt
Yellow#Junior
Orange#Senior
Yellor#Junior
Purple#Junior
Brown-2#Junior
White#Senior
Orange#Senior
Red#Junior
Red#Junior
Brown-2#Senior
Yellow#Senior
Red#Junior
Blue#Senior
Green#Senior
Purple#Junior
White#Junior
[root@www shell]# cat gs.awk
#!/bin/awk -f
BEGIN{
FS="#"
belt["Yellow"]
belt["Orange"]
belt["Red"]
student["Junior"]
student["Senior"]
}
{for (colour in belt){if($1==colour) belt[colour]++}}
{for (senior_or_junior in student) {if ($2==senior_or_junior) student[senior_or_junior]++}}
END{for (colour in belt ) print "The club has ",belt[colour],colour,"Belts"
for (senior_or_junior in student) print "The club has ", student[senior_or_junior],senior_or_junior,"student"}
[root@www shell]# ./gs.awk grade_student.txt
The club has  2 Orange Belts
The club has  3 Red Belts
The club has  2 Yellow Belts
The club has  7 Senior student
The club has  9 Junior student
BEGIN部分设置FS为符号#,即域分隔符
因为要查找Yellow、Orange和Red三个级别。然后在脚本中手工建立数组下标对学生做同样的操作
首先给数组命名为color,使用循环语句测试域1级别列是否等于数组元素之一(Yellow,Orange或Red)
如果匹配,依照匹配元素将运行总数保存进数组。运行总数存入junior或senior的匹配数组元素
END部分打印浏览结果,对每一个数组使用循环语句并打印它。

常用awk脚本:
1、批量添加用户:
[root@www shell]# cat useradd.awk
#!/bin/awk -f
BEGIN{
for (i=1;i<=10;i++)
{
system ("useradd user" i)
       system("echo 123 | passwd --stdin user" i)
} }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值