admin 管理员组文章数量: 887191
2023年12月21日发(作者:leftjoin出现重复数据)
《AWK详细介绍》
1:调用awk的三种方式如下:
1.1 命令行方式:
awk [-F filed-separator] 'commands' input-files
-F 指定分隔符(默认是空格或tab)
commands awk的命令
input-files 要处理的文件
1.2 将所有awk命令插入一个文件,并使awk程序可执行,然后用awk命令解释器作为脚本的首行,以便通过键入脚本名称来调用它
1.3 将所有的awk命令插入一个单独文件,然后调用:
awk -f awk-script-file input-files
-f 指明调用脚本名
input-files 待处理的文件
2: awk脚本的介绍
在命令中调用awk时,awk脚本由各种操作和模式组成。
awk每次读一条记录或一行,并使用指定的分隔符分隔指定域。当新行出现时,awk命令获悉已读完整条记录,然后在下一个记录启动读命令,这个读进程将持续到文件尾或文件不再存在。
2.1 模式和动作
任何awk语句都由模式和动作组成。模式部分决定动作语句何时触发及触发条件。处理即对数据进行的操作。如果省略模式部分,动作将时刻保持执行状态。
模式可以是任何条件语句、复合语句或正则表达式,包括两个特殊字段BEGIN和END。使用BEGIN语句设置计数和打印头。BEGIN语句用在任何文本浏览动作之前,之后文本浏览动作依据输入文件开始执行。END语句用在awk完成文本浏览动作后打印输出文本总数和结尾状态标志。如果不特别指明模式,awk总是匹配。
实际动作在大括号{}内指明。动作大多数用来打印,但还有些更长的代码诸如if和循环语句及循环退出结构。如果不指明采取动作,awk将打印出所有浏览出来的记录。
2.2 域和记录
awk执行时,其浏览域标记为$1,$2…$n,这种方法称为域标识。当需要指定多域时使用逗号,用于分隔,如$1,$3指定第一域和第三域,如果希望指定所有域可使用$0。
使用print命令执行打印操作,这是一个awk命令,需要用{}括起来。
2.2.1 抽取域
举例: 现有一个文件,内容如下:
$ cat
y 05/99 48311 Green 8 40 44
06/99 48317 Green 9 24 26
02/99 48 Yellow 12 35 28
07/99 4842 Brown-3 12 26 26
y 05/99 4712 Brwon-2 12 30 28
此文本文件有7个域,以空格作为域分隔符
2.2.2 保存awk输出
两种方式保存awk的输出结果:
重定向到文件:(屏幕无显示)
$ awk '{print $0}' >wow
使用管道将输出结果传给tee:(屏幕显示结果)
$ awk '{print $0}' | tee woww
2.2.3 使用标准输入
几种方法:
$
$ <
2.2.4 打印所有记录
$ awk '{print $0}'
2.2.5 打印单独记录
只打印$1和$4:
$ awk '{print $1,$4}'
2.2.6 自定义格式打印
可以在$1和$4的上方添加注释,Name和Belt:
$ awk 'BEGIN {print "NametBeltn-------------------------"} {print $1"t"$4}'
在结尾加上结束注释:
$ awk 'BEGIN {print "NametBeltn-------------------------"} {print $1"t"$4} END {print "-------------------------nEnd
of report"}'
2.2.7 awk错误信息提示
在碰到awk错误时,可相应查找:
• 确保整个awk命令用单引号括起来
• 确保命令内所有引号成对出现
• 确保用大括号{}括起动作语句,用括号()括起条件语句
• 可能忘记使用大括号{}
2.2.8 awk键盘输入
如果没有给出输入文件,awk从键盘获得输入,完成后按
2.3 元字符
【 ^ $ . [ ] | ( ) * + ?】
其中+和?只适用于awk而不适用于grep或sed:
+ 匹配一个或一个以上前面的单字符
匹配0个或一个前面的单字符
举例:
/XY+Z/ 匹配XYZ、XYYYYZ
/XY?Z/ 匹配XYZ、XZ
2.4 条件操作符
操作符 描述
【 < 小于】
【<= 小于等于】
【 == 等于】
【 != 不等于】
【> 大于】
【>= 大于等于】
【~ 匹配正则表达式】
【 !~ 不匹配正则表达式】
2.4.1 匹配
使用~紧跟正则表达式可以匹配域,也可以用if,条件需要用()括起来
举例:
查询棕带学员的所有情况(打印出$4匹配Brown的行)
$ awk '{if ($4~/Brown/) print $0}'
或
$ awk '$0 ~ /Brown/'
2.4.2 精确匹配
使用==并用双引号括起条件可以精确匹配条件
举例:
$ awk '{if ($3 == "48") print $0}'
2.4.3 不匹配
使用!~紧跟正则表达式不匹配域
举例:
查询不是棕带的所有学员情况(打印出$4不匹配Brown的行)
$ awk '{if ($4!~/Brown/) print $0}'
或
$ awk '$0 !~ /Brown/'
2.4.4 比较
由于几个比较大小符的使用方法都一样,就随便举一个例子:
$ awk '{if ($6 < $7) print $1" try better at the next comp"}'
2.4.5 各种匹配
匹配Green或green:
$ awk '/[Gg]reen/'
匹配$1的第四个字符是a:
$ awk '$1 ~ /^...a/'
匹配Yellow或Brown:
$ awk '$4 ~ /Yellow|Brown/'
匹配以J开头的行:
$ awk '$0 ~ /^J/'
2.4.6 复合表达式
复合模式或复合操作符用于形成复杂的逻辑操作,复合表达式即为模式间通过使用复合操作符互相结合起来的表达式
(&& 和 AND: 符号两边必须同时为真)
(|| 和 OR: 符号两边至少一边为真)
(! : 非 求逆意思)
举例:
$ awk '{if ($1 == "" && $4 == "Yellow") print $0}'
$ awk '{if ($4 == "Yellow" || $4 ~ /Brown/) print $0}'
2.5 awk内置变量说明
awk有许多内置变量用来设置环境信息,常用的有:
ARGC
ARGV
命令行参数个数
命令行参数排列
ENVIRON 支持队列中系统环境变量的使用
FILENAME
FNR
FS
NF
NR
OFS
RS/ORS
ARGC支持命令行中传入awk脚本的参数个数。ARGV是ARGC的参数排列数组,其中每一元素表示为ARGV[n],n为希望访问的命令行参数
ENVIRON支持系统设置的环境变量,要访问单独变量,使用实际变量名,如ENVIRON[“EDITOR”]=”Vi”
FILENAME为awk脚本当前操作文件的文件名
FNR为awk当前操作文件的记录行数,其值总是小于等于NR。如果脚本正在访问多个文件,每一新输入文件都将重置此变量
FS用来设置域分隔符,与命令行中-F相同,缺省为空格。如: 以冒号为分隔符FS=":"
NF为记录域个数(列数),在记录被读之后设置,常用$NF指定最后一列
OFS指定输出记录的域分隔符,缺省为空格。如: 以-为分隔符输出FS="-"
awk读取的文件名
当前文件已处理的记录行数
设置域分隔符,相当于-F
域的列数
已处理的记录总行数
设置输出字段域分隔符
换行符
ORS与RS都为输出记录分隔符,缺省是新行(n)
举例:
查看文件的记录数:
$ awk 'END {print NR}'
打印所有记录及记录的行号及总域数,并在最后打印出读取文件的文件名:
$ awk '{print NF,NR,$0} END {print FILENAME}'
至少包含一个记录且包含Brown:
$ awk '{if (NR > 0 && $4 ~ /Brown/) print $0}'
NF的一个强大功能是将变量$PWD的返回值传入awk并显示其目录:
$ echo $PWD | awk -F/ '{print $NF}'
显示文件名:
$ echo "/usr/apache/conf/" | awk -F/ '{print $NF}'
2.6 awk操作符使用
awk操作符的基本表达式可以划分为数字型、字符串型、变量型域及数组元素
【= += -= *= /= %= ^= 赋值操作符】
【? 条件表达操作符】
【|| && ! 并、与、非】
【~ !~ 匹配操作符(匹配与不匹配)】
【< <= == != > >= 关系操作符】
【+ - * / % ^ 算术操作符】
【++ -- 前缀和后缀】
2.6.1 设置输入域到域变量名
在awk中,设置有意义的域名是一种好习惯,在进行模式匹配或关系操作时更容易理解。一般的变量名设置方式为name=$n,这里name为调用的域变量名,n为实际域号。多个域名设置之间用分号;分隔(请注意下面;的使用)
$ awk '{name = $1;belts = $4; if (belts ~ /Yellow/) print name" is belt "belts}'
2.6.2 域值比较操作
有两种方式测试一数值域是否小于另一数值域:
1) 在BEGIN中给变量名赋值
2) 在关系操作符中使用实际数值
通常在BEGIN部分赋值是很有益的,可以在awk表达式进行改动时减少很多麻烦。使用关系操作时必须用圆括号括起来。举例:
查询所有比赛中得分在27点一下的学生:
$ awk '{if ($6 < 27) print $0}'
或
$ awk 'BEGIN {BASELINE = "27"} {if ($6 < BASELINE) print $0}'
2.6.3 修改数值域取值
当在awk中修改任何域时,重要的一点是要记住实际输入文件是不可修改的,修改的只是保存在缓存里的awk复本。awk会在变量NR或NF中反映出修改痕迹为修改数值域,简单的给域标识重赋新值。举例:
$ awk '{if ($1 == "y") $6 = $6 - 1; print $1,$6,$7}'
2.6.4 修改文本域
修改文本域即对其重新赋值,赋给一个新的字符串。记住,字符串要使用双引号,并用圆括号括起整个语法。举例:
$ awk '{if ($1 == "") ($1 = ""); print $1}'
2.6.5 只显示修改记录
如果文件很大,修改的记录很多,所以需要只查看修改部分。在模式后面使用大括号{}将只打印修改部分。取得模式,再根据模式结果实施操作。举例:(注意大括号的位置)
$ awk '{if ($1 == "") {$1 = ""; print $1}}'
2.6.6 创建新的输出域
在awk中处理数据时,基于各域进行计算时创建新域是一种好习惯。创建新域要通过其他域赋予新域标识符。举例:
在中创建新域8保存域目前级别分与域最高级别分的差值:
$ awk 'BEGIN {print "NametDifference"} {if ($6 < $7) {$8 = $7 - $6; print $1,$8}}'
或者赋予便于理解的变量名:
$ awk 'BEGIN {print "NametDifference"} {if ($6 < $7) {diff = $7 - $6; print $1,diff}}'
2.6.7 增加列值
使用符号+=增加列数或进行运行结果统计。将符号右边的变量域的值赋给左边。举例:
打印出所有学生的总成绩(将整个文件的$6求和,total为变量名):
$ awk '(total += $6); END {print "Club student total points: "total}'
如果文件很大,只想打印结果部分而不是所有记录,在语句的外面加上大括号{}即可:
$ awk '{(total += $6)}; END {print "Club student total points: "total}'
查看当前目录所有文件大小及总和(除去子目录):
$ ls -l|awk '/^[^d]/ {print $9"t"$5} {total += $5} END {print "total Bytes: "total}'
2.7 内置的字符串函数
awk有很多强大的字符串函数:
gsub(r,s,t) 在整个字符串t中用字符串s替代所有满足正则表达式r的字符串(如没指定t则默认为$0)
index(s,t) 返回字符串s中字符串t第一次出现的位置(如不存在则为0)
length(s) 返回s字符串的长度(如没指定s则默认为$0)
match(s,r) 返回第一个满足正则表达式r的字符串在s字符串里面的位置(如无匹配的则为0)
split(s,a,sep) 使用sep将字符串s分隔成为数组a的元素,返回数组元素的数量(如果没指定分隔符则默认与FS相同用法)
sprint("fmt",exp) 使用printf的格式说明对表达式exp 进行格式化输出
sub(r,s,t) 在字符串t中用s字符串替换满足正则表达式r的第一个字符串,返回1如果成功,否则返回0(如没指定t则默认为$0)
substr(s,p,n) 返回字符串s中以p位置开始长度为n的字符串(如果没指定n则返回从p位置开始的所有字符
串)
tolower(s) 将字符串s中的所有大写字母转换为小写,返回转换后的字符串
toupper(s) 将字符串s中的所有小写字母转换为大写,返回转换后的字符串
2.7.1 gsub(r,s,t)
在整个字符串t中用字符串s替代所有满足正则表达式r的字符串(如没指定t则默认为$0):
$ awk 'gsub(/4842/,4899) {print $0}'
或
$ awk '{if (gsub(/4842/,4899)) print $0}'
2.7.2 index(s,t)
返回字符串s中字符串t第一次出现的位置(如不存在则为0)。必须用双引号将字符串都括起来:
$ awk 'BEGIN {print index("Benny","ny")}'
2.7.3 length(s)
返回s字符串的长度(如没指定s则默认为$0):
$ awk '$1 == "" {print length($1)" "$1}'
$ awk 'BEGIN {print length("Kevin is a good boy.")}'
2.7.4 match(s,r)
返回第一个满足正则表达式r的字符串在s字符串里面的位置:
$ awk 'BEGIN {print match("ANCD",/d/)}' #(返回0)
$ awk '$1=="" {print match($1,/u/)}' #(返回第一个u在里的位置: 3)
2.7.5 split(s,a,sep)
使用sep将字符串s分隔成为数组a的元素,返回数组元素的数量:
$ awk 'BEGIN {print split("123#456#789",array,"#")}'
返回3,数组array的元素分别为:
array[1]="123"
array[2]="456"
array[3]="789"
2.7.6 sub(r,s,t)
在字符串t中用s字符串替换满足正则表达式r的第一个字符串,如果成功返回1,否则返回0(如没指定t则默认为$0):
$ awk '$1 == "" {if (sub(/26/,"29")) print}'
2.7.7 substr(s,p,n)
返回字符串s中以p位置开始长度为n的字符串(如果没指定n则返回从p位置开始的所有字符串):
$ awk '$1 == "y" {print substr($1,1,5)}' #(返回)
如果给定长度值n大于字符串长度,则awk将从起始位置返回所有字符:
$ awk '$1 == "y" {print substr($1,3,15)}' #(返回Tansley)
如果没指定n,则awk将从起始位置返回所有字符:
$ awk '{print substr($1,3)}'
在BEGIN部分定义字符串,在END部分返回抽取的字符:
$ awk 'BEGIN {STR="Kevin is a good boy."} END {print substr(STR,12,8)}'
2.7.8 从shell中向awk传入字符串
$ echo "Stand-by" | awk '{print length($0)}'
设置文件名为一变量,取文件名:
$ STR=""
$ echo $STR | awk '{print substr($STR,1,5)}'
取文件名后缀:
$ STR=""
$ echo $STR | awk '{print substr($STR,7) }'
2.8 awk输出函数printf
awk提供函数printf,拥有几种不同的格式化输出功能。每一种printf函数(控制格式字符)都以一个%符号开始,以一个决定转换的字符结束。转换包含三种修饰符。语法:
printf "格式控制符",参数
awk printf修饰符
【- 左对齐】
【Width 域的步长,用0表示0步长】
【.prec 最大字符串长度,或小数点右边的位数】
awk printf格式
【%c ASCII字符】
【%d 整数】
【%e 浮点数(科学记数法)】
【%f 浮点数】
【%g awk决定使用哪种浮点数转换(e或者f)】
【%o 八进制数】
【%s 字符串】
【%x 十六进制数】
举例:
字符转换:
$ awk 'BEGIN {printf "%cn",65}'
格式化输出:
打印所有的学生名字和序列号,要求名字左对齐,15个字符长度,后跟序列号:
$ awk '{printf "%-15s %sn",$1,$3}'
再加入注释:
$ awk 'BEGIN {print "NametNumber"} {printf "%-15s %sn",$1,$3}'
2.9 向一行awk命令传值
在awk执行前将值传入awk变量,需要将变量放在命令行下,格式:
awk 命令变量=输入文件值
举例:
$ awk '{if ($5 < AGE) print $0}' AGE=10
查询可用空间小于36000M的分区:
$ df –m | awk '($4 ~ /^[0-9]/) {if ($4 < LIMIT) print $6"t"$4}' LIMIT=36000
awk也允许传入环境变量。举例:
查询当前用户登陆在哪个端口:
$ who | awk '{if ($1 == user) print $1" you are connected to "$2}' user=$LOGNAME
2.10 awk脚本文件
可以将awk脚本写入一个文件再执行它。使用awk脚本的好处是不必每次使用时都需要重新输入,并且可以增加注释便于理解。拿一个awk脚本举例:
#!/bin/awk –f
# all comment lines must start with a hash '#'
# name: student_
# to call: student_
# prints total and average of club student points
# print a header first
BEGIN{
print "StudenttDatetNumbertGradetAgetPointstMax"
print "NamettJoinedttttGainedtPoint Available"
print "=============================================================="
}
# let's add the scores of points gained
(total += $6)
# finished processing now let's print the total and average point
END {
print "Club Student Total points: " total
print "Average Club Student points: " total/NR
}
第一行是#!/bin/awk -f,这一行很重要,没有它脚本将不能执行,因为它告知脚本系统中awk的位置。通过将命令分开,脚本的可读性提高,还可以在命令之间加入注释。
执行时在脚本文件后键入文件名,但需要先赋予可执行权限。
2.11 在awk中使用FS变量
使用awk脚本时,设置FS变量是在BEGIN部分。如果不这样,awk不知道域分隔符是什么。
脚本举例:(该脚本从/etc/passwd中抽取第一和第五域)
#!/bin/awk –f
# to call: /etc/passwd
# print out the first and fifth fields
BEGIN {
FS=":"}
{print $1,"t",$5}
2.12 向awk脚本传值
向awk脚本传值与向awk命令行传值方式大体相同,格式:
awk_script var=value input_file
举例:
#!/bin/awk –f
# name:
# to call: AGE=n
# prints ages that are lower than the age supplied on the command line
{if ($5 < AGE)
print $0}
执行方式:(别忘了先赋予可执行权限)
$ ./ AGE=10
也可以使用管道命令将命令的结果传递给awk脚本,如
$ cat | ./ student_
2.13 awk数组介绍
数组使用前不必定义,也不必指定数组元素个数。经常使用循环来访问数组,下面是一种循环类型的基本结构:
for (element in array) print array[element]
举例:(前面提到过的字符串123#456#789)
#!/bin/awk –f
# name:
# prints out an array
BEGIN {
record="123#456#789";
split(record,array,"#")
}
END {
for (i in array) {
print array[i]
}
}
使用/dev/null作为输入运行脚本:
$ ./ /dev/null
从指定文本里找出匹配条件的项及其出现的次数
#!/bin/awk –f
# name:
# to call:
# loops through the file and counts how many belts we have in(yellow, orange, red)
# also count how many adults and juniors we have
# start of BEGIN
# set FS and load the arrays with our values
BEGIN {FS="#"
# load the belt colours we are interested in only
belt["Yellow"]
belt["Orange"]
belt["Red"]
# end of BEGIN
# load the student type
student["Junior"]
student["Senior"]
}
# loop thru array that holds the belt colours against field-1
# if we have a match, keep a running total
{for (colour in belt)
{if ($1 == colour)
belt[colour]++}}
# loop thru array that holds the student type against
# field-2 if we have a match, keep a running total
{for (senior_or_junior in student)
{if ($2 == senior_or_junior)
student[senior_or_junior]++}}
# finished processing so print out the matches..for each array
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, "students"}
BEGIN部分设置域分隔符为#,初始化两个数组的内容元素。然后处理文件,先给数组命名为colour,使用循环语句比对$1是否等于数组元素之一(Yellow、Orange或Red),如果匹配,依照匹配元素将运行总数保存进数组。同样的方法处理数组senior_or_junior。END部分打印浏览结果,对每一个数组使用循环语句并打印。
以上是awk的一些基本用法,请按规则编写和测试使用,只要花时间,肯定能达到意想不到的结果。
版权声明:本文标题:Unix和Linux 的AWK详细介绍 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/free/1703164939h440573.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论