内容简介:awk命令在文件或字符串中基于指定规则浏览和抽取信息。awk是一种小巧的编程语言及命令行工具。(其名称得自于它的创始人Alfred Aho、Peter Weinberger 和 Brian Kernighan姓氏的首个字母)。它非常适合服务器上的日志处理,主要是因为awk可以对文件进行操作,通常以可读文本构建行。awk命令在文件或字符串中基于指定规则浏览和抽取信息。awk抽取信息后,才能进行其他文本操作,awk脚本通常用来格式化文本文件中的信息。
awk命令在文件或字符串中基于指定规则浏览和抽取信息。
命令功能
awk是一种小巧的编程语言及命令行工具。(其名称得自于它的创始人Alfred Aho、Peter Weinberger 和 Brian Kernighan姓氏的首个字母)。它非常适合服务器上的日志处理,主要是因为awk可以对文件进行操作,通常以可读文本构建行。
awk命令在文件或字符串中基于指定规则浏览和抽取信息。awk抽取信息后,才能进行其他文本操作,awk脚本通常用来格式化文本文件中的信息。
命令格式
有三种方式调用awk,第一种是命令行方式,例如:
awk [-F field-separator] 'commands' input-file(s)
awk默认使用空格作为缺省的域分隔符。如果要浏览诸如passwd文件,此文件是以冒号作为分隔符,则必须指明-F选项。例如:
awk -F : 'commands' input-file
第二种方式是将所有awk命令插入一个文件,并使awk程序可执行,然后用awk命令解释器作为脚本的首行,以便通过键入脚本名称来调用它。
第三种方式是将所有的awk命令插入一个单独文件,然后调用:
awl -f awk-script-file input-file(s)
-f选项指明在文件awk-script-file中的awk脚本,input_file(s)是使用awk进行浏览的文件名。
awk脚本
代码结构
awk脚本的代码结构很简单,就是一系列的模式(pattern)和动作(action)。
# comment Pattern1 { ACTIONS; } # comment Pattern2 { ACTIONS; } # comment Pattern3 { ACTIONS; } # comment Pattern4 { ACTIONS; }
扫描文档的每一行时都必须与每一个模式进行匹配比较,一次只匹配一个模式。
this is line 1 this is line 2
this is line 1这行会先Pattern1进行匹配,如果匹配成功,就会执行ACTIONS。然后this is line 1会和Pattern2进行匹配,如果匹配失败,就调到Pattern3进行匹配,以此类推。
一旦所有的模式都匹配过了,this is line 2就会以同样的步骤进行匹配。其他的行也一样,直到读取完整个文件。这就是awk的运行模式。
数据类型
awk仅有两个主要的数据类型:字符串和数字,它们可以相互转换。
在ACTIONS部分使用=操作符给变量赋值,可以在任意时刻、任意地方声明和使用变量,也可以使用未初始化的变量,默认是空字符串。
awk有数组类型,并且它们是动态的一维关联数组。
模式
模式分为三大类:正则表达式、布尔表达式和特殊模式。
所有模式都是可选的,下面的脚本形式会对输入的每一行都会简单地执行ACRIONS。
{ ACTIONS }
特殊的模式
模式包括两个特殊字段:BEGIN和END。BEGIN在所有输入未被处理之前,即文本浏览动作之前进行匹配。可以初始化脚本变量和所有种类的状态的主要地方。END会在所有的输入都被处理完后,即完成文本浏览动作后进行匹配。可以在退出前进行清除工作和一些最后的输出。
最后一类模式,要把它进行归类有点困难。它处于变量和特殊值之间,我们通常称它们为域(Field)。而且名副其实。
域
# According to the following line # # $1 $2 $3 # 00:34:23 GET /foo/bar.html # _____________ _____________/ # $0 # Hack attempt? /admin.html$/ && $2 == "DELETE" { print "Hacker Alert!"; }
域(默认地)由空格分隔。$0域代表了一整行的字符串。$1 域是第一块字符串(在任何空格之前),$2\$域是后一块,以此类推。
awk执行时,其浏览域标记为$1, $2, $3…$n。这种方式称为域标识。使用$1, $3标识表示第1和第3域。使用$0$标识表示所有域。
awk浏览到一新行时,即到达域的记录末尾,执行新记录下一行的读动作,重新设置域分隔。
动作
最常用和最有用的行为:
{ print $0; } # prints $0. In this case, equivalent to 'print' alone { exit; } # ends the program { next; } # skips to the next line of input { a=$1; b=$0 } # variable assignment { c[$1] = $2 } # variable assignment (array) { if (BOOLEAN) { ACTION } else if (BOOLEAN) { ACTION } else { ACTION } } { for (i=1; i<x; i++) { ACTION } } { for (item in c) { ACTION } }
awk里的变量都是全局变量。
函数
函数的 通用文档(regular documentation)
{ somecall($2) }
用户定义的函数:
# function arguments are call-by-value function name (parameter-list) { ACTIONS; #same actions as usual } # return is valid keyword function add (val) { return val+1; }
实用命令
实例:0. 新建测试文件
描述:新建一个device文件,其中(1)为序号,(2)为Android版本,(3)为访问时间,(4)为IP,(5)为访问次数。本文大部分实例根据这一文件进行说明。
输出:
实例:1. 抽取域
描述:打印第1个(序号)域和第2个(Android版本)域的内容。print用来输出其后跟着的内容,用大括号把print语句括起来,表示一个打印动作。
输出:
实例:2. 打印所有记录
描述:打印所有记录。$0代表所有域。
命令:awk '{print $0}' device
输出:
实例:3. 打印报告头
描述:在序号和IP地址之间用一些空格使之更容易划分,也可以在域间使用tab键加以划分。本例中加入NO和IP两个信息头以及中划线,\n启动新行,并在\n下一行启动打印文本操作。打印信息头放置在BEGIN模式部分,因为打印信息头被界定为一个动作,必须用大括号括起来。在awk查看第一条记录前,信息头被打印。
命令:awk 'BEGIN {print "NO IP\n------------------------"} {print $1"\t"$4}' device
输出:
实例:4. 打印信息尾
描述:在末行加入end of report信息。END语句在所有文本处理动作执行完之后才被执行,在脚本中的位置是在主要动作之后。
命令:awk 'BEGIN {print "Version\n-------"} {print $2} END {print "end-of-report"}' device
输出:
实例:5. 错误信息提示
描述:如果将在awk命令中缺少一个双引号,awk将返回错误提示信息。
命令:awk 'BEGIN {print "Version\n-------"} {print $2} END {print "end-of-report}' device
输出:
注意:在碰到awk错误时,应从以下几点进行排查:
- 确保整个awk命令引用单引号括起来。
- 确保命令内所有引号成对出现。
- 确保用花括号括起动作语句,用圆括号括起条件语句。
- 可能忘记使用花括号。
描述:如果查询的文件不存在,将得到以下错误信息:
命令:awk 'END {print NR}' device.txt
输出:
条件操作符
实例:1. 匹配
描述:如果field-4以数字4开头,打印它。如果条件满足,则打印匹配的记录行。符号~后紧跟正则表达式,使一域号匹配正则表达式,也可以使用if语句。awk的if后面的条件用()括起来。^尖角符号表示行首。
命令:awk '{ if ($4 ~ /^4/) print $0}' device
输出:
等同于:
实例:2. 精确匹配
描述:精确匹配访问次数为1次的记录,确保不匹配访问次数为15次的记录。使用等号==,并用单引号括起条件,也可以使用if语句。
命令:awk '$5=="1" {print $0}' device
或者:
awk '{if($5==/1/) print $0}' device
输出:
实例:3. 不匹配
描述:不匹配IP地址以4开头的记录。使用!~表示不匹配。
命令:awk '$4 !~ /^4/' device
或者:
awk '{ if ($4 !~ /^4/) print $0}' device
输出:
注意这里不能用!=,因为用引号或者/括起了^4,将只匹配4而不匹配49.65.119.165等。如果查询非49.65.119.165的记录,可做如下操作:
awk '$4 != "49.65.119.165"' device
实例:4. 小于,小于等于,大于,大于等于
描述:匹配访问次数小于序号的记录。同样的有小于等于(<=),大于(>),大于等于(>=)。
命令:awk '$4 !~ /^4/' device
或者:
awk '{ if ($4 !~ /^4/) print $0}' device
输出:
实例:5. 设置大小写
描述:匹配含有前面是i或I,后面是OS的记录。[]符号可匹配[]内任意字符或单词。
命令:awk '/[iI]OS/' device
输出:
实例:6. 任意字符
描述:匹配Android版本,第八个字符是7,打印它。表达式/^…….7/表示行首前7个字符任务,第八个是7。
命令:awk '$2 ~ /^.......7/' device
输出:
实例:7. 或关系匹配
描述:匹配IP地址以4或者3开头的记录。竖线符|意为两边模式之一。可以得到与[]表达式相同的结果。
命令:awk '$4 ~ /^(4|3)/' device
输出:
注意,在使用竖线符时,语句必须用圆括号括起来。另外,除了字符重复出现外,其他的正则表达式在awk中都是合法的。
实例:8. AND
描述:匹配Android版本在7.0以上,并且IP地址以4开头的记录。OR,非与之类似。
命令:awk '$2 ~ /^.......7/ && $4 ~ /^4/' device
等同于:
awk '{ if ($2 ~ /^.......7/ && $4 ~ /^4/) print $0} ' device
输出:
awk内置变量
awk内置变量如下:
BEGIN { # Can be modified by the user FS = ","; # Field Separator RS = "n"; # Record Separator (lines) OFS = " "; # Output Filed Separator ORS = "n"; # Output Record Separator (lines) } { # Can't be modified by the user NF # Number of Fileds in the current Record (lines) NR # Number of Records seen so far ARGV / ARGC # Script Arguments }
NF:支持记录域个数,在记录被读之后再设置。
NR:已读的记录数。
FILENAME:告知系统目前正在浏览的实际文件,因为awk可以同时处理许多文件。
实例:1. NF、NR、FILENAME
描述:所有记录被打印,并带有记录号(第二和第三列),并在最后输出文件名。使用NF变量显示每一条读记录中有多少个域(5个),使用NR显示已读的记录数,使用FILENAME显示正在处理的文件名。
命令:awk '{print NF,NR,$0} END {print FILENAME}' device
输出:
实例:2. 判断文件至少有一个记录
描述:先检查文件中至少有一个记录时才查询IP地址。
命令:awk 'NR > 0 && $4 ~ /^4/' device
输出:
实例:3. 与echo结合使用
描述:将变量$PWD的返回值传入awk并显示其目录。需要指定域分隔符/。
命令:echo $PWD | awk -F / '{print $NF}'
输出:
描述:显示文件名。
命令:echo "/etc/vimrc" | awk -F / '{print $NF}'
输出:
awk操作符
实例:1. 设置输入域到域变量名
描述:赋值IP地址域为ip,版本域为version,查询版本大于7的记录,并打印IP地址和版本信息。
命令:awk '{ip=$4;version=$2; if (version ~ /*7*/) print ip""version}' device
输出:
实例:2. 域值比较操作
有两种方式测试数值域是否小于另一数值域。
- 在BEGIN中给变量名赋值。
- 在关系操作中使用实际数值。
描述:找出访问次数大于10次的所有记录。
命令:awk '{if ($5 > 10) print $0}' device
输出:
实例:3. 修改数值域的值
当在awk中修改任何域时,实际输入文件是不可修改的,修改的只是保存在缓存里的awk副本,awk会在变量NR或NF变量中反映出修改痕迹。
描述:修改序号为6的记录,将其访问次数减一。
命令:awk '{if ($1==6) $5=$5-1; print $1, $2, $5 }' device
输出:
实例:4. 修改文本域
描述:修改序号为6的记录,将其版本修改为iOS11.2.3。修改文本域就是对其重新赋值。
命令:awk '{if ($1==6) ($2="iOS11.2.3"); print $1, $2, $5 }' device
输出:
实例:5. 只显示修改记录
描述:只显示修改后序号为6的记录。
命令:awk '{if ($1==6) {$2="iOS11.2.3"; print $2}; }' device
输出:
实例:6. 创建新的输出域
描述:创建新域6保存目前访问次数大于序号的减法值,表达式为’{$6=$5-$1}’,只打印其值大于零的序号和其新域值。在BEGIN部分加入tab键以对齐报告头。也可以赋给新域更有意义的变量名。
命令:awk 'BEGIN {print "IP\t Difference"} {if ($5 > $1) {$6=$5-$1; print $1 "\t" $6}}' device
输出:
实例:7. 增加列值
描述:使用+=累加访问次数的值。awk的每一个操作匹配时,如果没有说明打印记录,那默认会打印所有记录。
命令:awk '(total+=$5); END {print "total visits : " total}' device
输出:
实例:8. 文件长度相加
描述:查看当前目录中所有文件的长度及其综合,但要排除子目录,使用ls -l命令,然后管道输出到awk,awk首先剔除首字符d(/^[^d]/)的记录,然后将文件长度相加,并输出每一文件长度及在END部分输出所有文件的长度。
命令:ls -l | awk '/^[^d]/ {print $9"\t"$5} {total+=$5} END {print "total KB: " total}'
输出:
内置字符串函数
gsub类似于sed查找和替换。它允许替换一个字符串或字符为另一个字符串或字符,并以正则表达式的形式执行,第一个函数作用于记录$0,第二个gsub函数允许指定目标,如果未指定,默认是$0。
index(s, t)函数返回目标字符串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)函数返回字符串s在位置p后长度为n的后缀部分。
实例:1. gsub
描述:匹配记录中访问时间为11:35的记录,修改为11:40。注意要用双引号括起来。
命令:awk 'gsub(/11:35/, "11:40") {print $0}' device
输出:
实例:2. index
描述:匹配字符串Honey中,ney子串第一次出现的位置,即字符个数。
命令:awk 'BEGIN {print index("Honey", "ney")}'
输出:
实例:3. length
描述:匹配序号为6,第二个域的字符长度。也可以直接使用字符串。
命令:awk '$1==6 {print length($2) "---" $2}' device
输出:
实例:4. match
描述:match测试目标字符串是否包含查找字符的一部分,可以使用正则表达式。
命令:
在AWK中查找d,因其不存在,所以返回0。
awk 'BEGIN {print match("AWK", /d/)}'
在AWK中查找K,因其存在,所有返回AWK中K出现的首位置字符数。
awk 'BEGIN {print match("AWK", /K/)}'
在序号为6的记录中,查找Android的大版本号。
awk '$1==6 {print match($2, "7")}' device
输出:
实例:5. split
描述:如果域中具有分隔符形式的字符串,使用split函数将其分隔,并保存到一个数组中,最后将数组的第一个元素打印出来。
命令:awk 'BEGIN {print split("123#456#789", myarray, "#")}'
输出:
实例:6. sub
描述:匹配所有Android,替换为android。注意只在模式第一次出现时进行替换操作。
命令:awk 'sub(/Android/, "android")' device
输出:
实例:7. substr
描述:匹配第二个域版本信息中,打印从第一个字符开始到第七个字符。如果给定的长度值远大于字符串长度,awk将从起始位置返回所有字符。另一种形式是返回字符串后缀或指定位置后面的字符。
命令:awk '$1==5 {print substr($2,1,7)}' device
输出:
实例:8. 从shell向awk传入字符串
命令:
使用管道将字符串powerful传入awk,返回其长度。
echo "powerful" | awk '{print length($0)}'
设置文件名为一变量,管道输出到awk,但会不带扩展名的文件名。
STR="myawk.txt" | echo $STR | awk '{print substr($STR,1,5)}'
设置文件名为一变量,管道输出到awk,只返回其扩展名。
TR="myawk.txt" | echo $STR | awk '{print substr($STR,7)}'
输出:
字符转义
printf修饰符
基本语法: printf([格式控制符], 参数)
格式控制符通常在引号里。
awkprintf修饰符:
awk printf格式:
实例:1. 字符转换
描述:通过管道输出65到awk中,printf进行ASCII码字符转换。
命令:
echo "65" | awk '{printf ("%c\n", $0)}'
或者
awk 'BEGIN {printf "%c\n", 65}'
输出:
描述:数字1024转换为浮点数之后,被加入了六个小数点。
命令:
awk 'BEGIN {printf "%f\n", 1024}'
输出:
实例:2. 格式化输出
描述:BEGIN后的第一个花括号嵌入头信息,第二个花括号打印所有用户的IP地址和访问时间,要求IP地址左对齐,23个字符长度,后跟访问时间。
命令:
awk 'BEGIN {print "IP\t\t\tTime"} {printf "%-23s %s\n", $4, $3}' device
输出:
实例:3. 向一行awk命令传值
描述:在命令行中设置VISITS等于10,然后传入awk中,查询访问次数大于10的所有记录。
命令:awk '{if($5 > VISITS) print $0} ' VISITS=10 device
输出:
描述:用管道将df -k传入awk,然后抽出第四列,即剩余可利用空间容量。使用$4 ~ /^[0-9]/取得容量数值,最后对命令行if($4 < TRIGGER)上变量TRIGGER的值进行查询。
查看文件系统空间容量,观察其是否达到一定水平。因为要监视的已使用空间容量不断在变化,所以需要再命令行指定一个触发值。
命令:df -k | awk '($4 ~ /^[0-9]/) {if ($4 < TRIGGER) printf "%-15s %s\n",$6,$4}' TRIGGER=930000
输出:
描述:打印当前注册用户,并加入一定信息。
命令:who | awk '{print $1 " is logged on"}'
输出:
描述:传入环境变量LOGNAME,显示当前用户名。
命令:who | awk '{if ($1 == user) print $1" you are connected to " $2}' user=$LOGNAME"}'
输出:
实例:4. awk脚本文件
描述:第一行#! /usr/bin/awk -f告知脚本系统awk命令的位置。在脚本文件后键入文件名之前,需要先对脚本文件加入可执行权限。
命令:chmod u+x user_tot.awk
user_tot.awk脚本文件:
描述:执行user_tot.awk脚本文件。
命令:./user_tot.awk device
输出:
实例:5. 在awk中使用FS变量
描述:从/etc/passwd文件中抽取第1和第5域,通过FS变量,指定冒号:分隔passwd文件域。第1域时账号名,第5域是账号所有者。
命令:chmod u+x passwd.awk | ./passwd.awk /etc/passwd
输出:
实例:6. 向awk脚本传值
向awk脚本传值与向awk一行命令传值的方式大体相同,格式为:
awk script_file var=value input_file
描述:对比检查文件中域号和指定数字。注意不要忘了增加脚本的可执行权限。
命令:chmod u+x fieldcheck.awk | ./fieldcheck.awk MAX=7 FS=":" /etc/passwd
输出:
描述:从du命令获得输入,并输出块和字节数。
命令:chmod u+x duawk.awk | du /root | ./duawk.awk
输出:
实例:9. awk数组
描述:用split将123#456#789划分开,并存入myarray数组,再使用循环打印各数组元素。
命令:chmod u+x duawk.awk | du /root | ./duawk.awk
输出:
实例:10. 处理由通配符指定的多个文件名
描述:打印当前目录中以.txt结尾的文件。nextfile告诉awk停止处理当前的输入文件。下一个输入记录读取来自下一个输入文件。
命令:
awk '{ print FILENAME; nextfile } ' *.txt
awk 'BEGIN{ print "Starting..."} { print FILENAME; nextfile }END{ print "....DONE"} ' *.txt
输出:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 简述awk命令及用法
- Linux sort命令用法详解
- Linux下diff命令用法详解
- Linux下mv命令高级用法
- Oracle的DBV命令行工具用法详解
- 进一步学习 nox 教程,轻松掌握命令行用法
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Base64 编码/解码
Base64 编码/解码
URL 编码/解码
URL 编码/解码