9.gawk应用实现
(1)内建变量; 数据字段变量: 允许你使用美元符号($)和数据字段在数据行中位置对应的数值来引用该数据行中的字段 字段分隔符: 默认情况下,字段分隔符是一个空白字符,也就是空格符或制表符(tab)。在命令行下使用命令行参数-F或者在gawk程序中使用特殊的内置变量FS来更改字段分隔符 变量汇总: FIELDWIDTHS 由空格分隔开的定义了每个数据字段确切宽度的一列数字 FS 输入字段分隔符 RS 输入数据行分隔符 OFS 输出字段分隔符 ORS 输出数据行分隔符 变量FS和OFS定义了gawk如何处理数据流中的数据字段 [root@ahui ~]# cat data1 data11,data12,data13,data14,data15 data21,data22,data23,data24,data25 data31,data32,data33,data34,data35 [root@ahui ~]# awk 'BEGIN{FS=","; OFS="-"} {print $1,$2,$3}' data1 data11-data12-data13 data21-data22-data23 data31-data32-data33 FIELDWIDTHS变量允许你读取数据行,而不用字段分隔符来划分字段。一旦设置了FIELDWIDTHS变量,gawk就会忽略FS变量,而根据提供的字段宽度大小来计算字段 [root@ahui ~]# cat data1b 1005.3847887.37 133-2.374893.23 23987.8237488.9 [root@ahui ~]# awk 'BEGIN{FIELDWIDTHS="3 5 2 5"} {print $1,$2,$3,$4}' data1b 100 5.384 78 87.37 133 -2.37 48 93.23 239 87.82 37 488.9 默认情况下,gawk将RS和ORS设为换行符,但是有时会碰到数据流中字段占了多行的情况。 经典的例子就是包含地址和电话号码的数据,其中地址和电话号码各占一行 [root@ahui ~]# cat data2 Riley Mullen 123 main street Chicago, IL 60601 (312)555-2378 Frank Williams 123 main street Chicago, IL 60601 (312)234-3253 Haley Snell 123 main street Chicago, IL 60601 (312)123-2374 [root@ahui ~]# gawk 'BEGIN{FS="\n"; RS=""} {print $1,$4}' data2 Riley Mullen (312)555-2378 Frank Williams (312)234-3253 Haley Snell (312)123-2374 只需把FS变量设置成换行符。这就表明数据流中的每行都是一个单独的字段,每行上的所有数据都属于同一个字段。 接着只需把RS变量设置成空字符串,然后在数据行间留一个空白行。gawk会把每个空白行当做一个数据行分隔符 2)数据变量: ARGC 当前命令行参数个数 ARGIND 当前文件在ARGV中的位置 ARGV 包含命令行参数的数组 CONVFMT 数字的转换格式(参见printf语句);默认值为%.6 g ENVIRON 当前shell环境变量及其值组成的关联数组 ERRNO 当读取或关闭输入文件发生错误时的系统错误号 FILENAME 用作gawk输入数据的数据文件的文件名 FNR 当前数据文件中的数据行数 IGNORECASE 设成非零值时,忽略gawk命令中出现的字符串的字符大小写 NF 数据文件中的字段总数 NR 已处理的输入数据行数目 OFMT 数字的输出格式;默认值为%.6 g RLENGTH 由match函数所匹配的子字符串的长度 RSTART 由match函数所匹配的子字符串的起始位置 相关实例: [root@ahui ~]# gawk 'BEGIN{print ARGC,ARGV[1]}' data1 2 data1 注意:ARGC变量表明命令行上有两个参数,这包括gawk命令和data1参数(记住,程序脚本并不算参数)。 ARGV数组从代表该命令的索引0开始,第一个数组值是gawk命令后的第一个命令行参数。 [root@ahui ~]# gawk ' > BEGIN{ > print ENVIRON["HOME"] > print ENVIRON["PATH"] > }' /root /usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin 注意:ENVIRON变量使用关联数组来提取shell环境变量。关联数组用文本作为数组的索引值,而不用数值。可用这种方式来从shell中提取任何环境变量的值来在gawk程序中使用。 [root@ahui ~]# gawk 'BEGIN{FS=":"; OFS=":"} {print $1,$NF}' /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 mail:/sbin/nologin uucp:/sbin/nologin operator:/sbin/nologin games:/sbin/nologin gopher:/sbin/nologin ftp:/sbin/nologin nobody:/sbin/nologin dbus:/sbin/nologin usbmuxd:/sbin/nologin vcsa:/sbin/nologin rpc:/sbin/nologin rtkit:/sbin/nologin avahi-autoipd:/sbin/nologin abrt:/sbin/nologin rpcuser:/sbin/nologin nfsnobody:/sbin/nologin haldaemon:/sbin/nologin gdm:/sbin/nologin ntp:/sbin/nologin apache:/sbin/nologin saslauth:/sbin/nologin postfix:/sbin/nologin pulse:/sbin/nologin sshd:/sbin/nologin tcpdump:/sbin/nologin ahui:/bin/bash 注意:NF变量允许你指定数据行中的最后一个数据字段,即NF变量含有数据文件中最后一个数据字段的数字值。可以在它前面加个美元符将它用作字段变量。 [root@ahui ~]# gawk ' > BEGIN{FS=","} > {print $1, "FNR="FNR, "NR="NR} > END{print "There were",NR,"records processed"}' data1 data1 data11 FNR=1 NR=1 data21 FNR=2 NR=2 data31 FNR=3 NR=3 data11 FNR=1 NR=4 data21 FNR=2 NR=5 data31 FNR=3 NR=6 There were 6 records processed 注意:FNR变量的值在gawk处理第二个文件时被重置了,而NR变量则在进入第二个数据文件后继续计数 注意变量赋值的语法结构:"FNR="FNR。引号位置错了会出现语法错误
(2)自定义变量 [root@ahui ~]# gawk ' > BEGIN{ > testing="This is a test" > print testing > }' This is a test 注意:gawk编程语言包含了用来处理数字值的标准数学操作符,其中包括求余符号(%)和幂运算符号(^或**) 2)在命令行上给变量赋值 [root@ahui ~]# cat script1 BEGIN{FS=","} {print $n} [root@ahui ~]# gawk -f script1 n=2 data1 data12 data22 data32 这个特性允许你改变脚本的行为而不需要修改实际的脚本代码。 使用命令行参数来定义变量值会有个问题。在你设置了变量后,这个值在代码的BEGIN部分不可用 [root@ahui ~]# cat script2 BEGIN{print "The starting value is",n; FS=","} {print $n} [root@ahui ~]# gawk -f script2 n=3 data1 The starting value is data13 data23 data33 可以用-v命令行参数来解决这个问题。它允许你指定在BEGIN代码部分之前设定的变量。在命令行上,-v命令行参数必须放在脚本代码之前 [root@ahui ~]# gawk -v n=3 -f script2 data1 The starting value is 3 data13 data23 data33 (3)处理数组 gawk编程语言使用关联数组来提供数组功能,关联数组跟数字数组不同之处在于它的索引值可以是任意文本字符串。每个索引字符串都必须是唯一的,并唯一地标识赋给它的数据元素。 1)定义数组变量 数组变量赋值的格式如下; var[index] = element 其中var是变量名,index是关联数组的索引值,element是数据元素值。 [root@ahui ~]# gawk 'BEGIN{ > capital["Illinois"] = "Springfiled" > print capital["Illinois"] > }' Springfiled 2)遍历数组变量 可以用for语句的一种特殊形式: for (var in array) { statements } 这个for语句会在每次将关联数组array的下一个索引值赋给变量var时,执行一遍statements。重要的是记住这个变量是索引值而不是数据元素值。 [root@ahui ~]# gawk 'BEGIN{ > var["a"] = 1 > var["b"] = 2 > var["c"] = 3 > var["d"] = 4 > for (test in var) > { > print "Index:",test," - value:",var[test] > } > }' Index: a - value: 1 Index: b - value: 2 Index: c - value: 3 Index: d - value: 4 3)删除数组变量 从关联数组中删除数组索引要用一个特别的命令: delete array[index] 删除命令会从数组中删除关联索引值和相关的数据元素值 [root@ahui ~]# gawk 'BEGIN{ > var["a"] = 1 > var["g"] = 2 > for (test in var) > { > print "Index:",test," - value:",var[test] > } > delete var["g"] > print "---" > for (test in var) > print "Index:",test," - Value:",var[test] > }' Index: a - value: 1 Index: g - value: 2 --- Index: a - Value: 1 (4)使用模式 1)正则表达式 在使用正则表达式时,正则表达式必须出现在它要控制的程序脚本的左花括号前: [root@ahui ~]# gawk 'BEGIN{FS=","} /11/{print $1}' data1 data11 2)匹配操作符 匹配操作符(matching operate)允许将正则表达式限定在数据行中的特定数据字段。匹配操作符是波浪线(~)。 $1 ~ /^data/ $1变量代表数据行中的第一个数据字段,这个表达式会过滤出第一个字段以文本data开头的所有数据行。 [root@ahui ~]# gawk 'BEGIN{FS=","} $2 ~ /^data2/{print $0}' data1 data21,data22,data23,data24,data25 也可以使用!符号来排除正则表达式的匹配: $1 !~ /expression/ [root@ahui ~]# gawk '$1 !~ /root/{print $1,$NF}' /etc/passwd bin:x:1:1:bin:/bin:/sbin/nologin bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin mail:x:8:12:mail:/var/spool/mail:/sbin/nologin uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin gopher:x:13:30:gopher:/var/gopher:/sbin/nologin gopher:x:13:30:gopher:/var/gopher:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin nobody:x:99:99:Nobody:/:/sbin/nologin nobody:x:99:99:Nobody:/:/sbin/nologin dbus:x:81:81:System bus:/:/sbin/nologin usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin vcsa:x:69:69:virtual owner:/dev:/sbin/nologin rpc:x:32:32:Rpcbind Daemon:/var/cache/rpcbind:/sbin/nologin rtkit:x:499:497:RealtimeKit:/proc:/sbin/nologin rtkit:x:499:497:RealtimeKit:/proc:/sbin/nologin avahi-autoipd:x:170:170:Avahi Stack:/var/lib/avahi-autoipd:/sbin/nologin abrt:x:173:173::/etc/abrt:/sbin/nologin abrt:x:173:173::/etc/abrt:/sbin/nologin rpcuser:x:29:29:RPC User:/var/lib/nfs:/sbin/nologin nfsnobody:x:65534:65534:Anonymous User:/var/lib/nfs:/sbin/nologin haldaemon:x:68:68:HAL daemon:/:/sbin/nologin gdm:x:42:42::/var/lib/gdm:/sbin/nologin gdm:x:42:42::/var/lib/gdm:/sbin/nologin ntp:x:38:38::/etc/ntp:/sbin/nologin ntp:x:38:38::/etc/ntp:/sbin/nologin apache:x:48:48:Apache:/var/www:/sbin/nologin apache:x:48:48:Apache:/var/www:/sbin/nologin saslauth:x:498:76:Saslauthd user:/var/empty/saslauth:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin pulse:x:497:496:PulseAudio Daemon:/var/run/pulse:/sbin/nologin sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin tcpdump:x:72:72::/:/sbin/nologin tcpdump:x:72:72::/:/sbin/nologin ahui:x:500:500:ahuihaerbin:/home/ahui:/bin/bash ahui:x:500:500:ahuihaerbin:/home/ahui:/bin/bash 3)数学表达式 可以使用任意的普通股数学比较表达式 x == y:值x等于y x <= y: x < y x >= y x > y 可以对文本数据使用表达式,但是它跟正则表达式不同,表达式必须完全匹配。 [root@ahui ~]# gawk -F, '$1 == "data"{print $1}' data1 [root@ahui ~]# gawk -F, '$1 == "data11"{print $1}' data1 data11
(5)结构化命令 [root@ahui ~]# cat data4 10 5 13 50 34 [root@ahui ~]# gawk '{ > if ($1 > 20) > { > x = $1 * 2 > print x > } > }' data4 100 68 gawk的if语句也支持else子句,允许在if语句条件不成立的情况下执行一条或多条语句 [root@ahui ~]# gawk '{ > if ($1 > 20) > { > x = $1 * 2 > print x > } else > { > x = $1 / 2 > print x > }}' data4 5 2.5 6.5 100 68 也可以在单行上使用else子句,但必须在if语句部分使用分号: if (condition) statement1; else statement2 [root@ahui ~]# gawk '{if ($1 > 20) print $1 * 2; else print $1 / 2}' data4 5 2.5 6.5 100 68 2)while语句 while语句的格式: while (condition) { statements } while循环允许遍历一组数据,并检查结束迭代的条件 [root@ahui ~]# gawk '{ total = 0 i = 1 while (i < 4) { total += $i i++ } avg = total / 3 print "Average:" ,avg }' data5 Average: 128.333 Average: 137.667 Average: 176.667 [root@ahui ~]# gawk '{ > total = 0 > i = 1 > while (i < 4) > { > total += $i > if (i == 2) > break > i++ > } > avg = total / 2 > print "The average of the first two data elements is:",avg > }' data5 The average of the first two data elements is: 125 The average of the first two data elements is: 136.5 The average of the first two data elements is: 157.5 3)do-while语句 do-while语句类似于while语句,但会在检查条件语句之前执行命令。格式为: do { statement } while (condition) 这种格式保证了语句会在条件被评估之前至少执行一次 4)for语句 gawk支持C风格的for循环: for( variable assignment; condition; iteration process)
(6)格式化打印 因此要用到格式化打印,我们需要printf命令。格式: printf "format string", var1, var2... format string是格式化输出地关键。它会用到文本元素和格式化指定符来具体指定如何呈现格式化输出。 格式化指定符采用如下格式: %[modifier]control-letter 其中control-letter是指明显示什么类型数据值的单字符码,而modifier定义了另一个可选的格式化特性。 c 将一个数作为ASCII字符显示 d 显示一个整数值 i 显示一个整数值(跟d一样) e 用科学计数法显示一个数 f 显示一个浮点数 g 用科学计数法或浮点数中较短的显示 o 显示一个八进制值 s 显示一个文本字符串 x 显示一个十六进制值 X 显示一个十六进制值,但用大写字母A-F 除了控制字母外,还有3种修饰符可以用来进一步控制输出 width: 指定了输出字段最小宽度的数字值。如果输出短于这个值,printf会向右对齐,并用空格来填充这段空间。如果输出比指定的宽度还要长,它就会覆盖width值。 prec: 指定了浮点数中小数点后面位数的数字值,或者文本字符串中显示的最大字符数。 -(减号): 减号指明在向格式化空间中放入数据时采用左对齐而不是右对齐 [root@ahui ~]# gawk 'BEGIN{FS="\n"; RS=""} {printf "%s %s\n", $1, $4}' data2 Riley Mullen (312)555-2378 Frank Williams (312)234-3253 Haley Snell (312)123-2374 注意:我们需要自己手动在printf命令的末尾添加换行符来生成新行。没加的话,printf命令会继续用同一行来打印后续输出。 如果你需要用几个单独的printf命令来在同一行上打印多个输出,它会非常有用: [root@ahui ~]# gawk 'BEGIN{FS=","} {printf "%s ", $1} END{printf "\n"}' data1 data11 data21 data31 接着我们用修饰符来格式化第一个字符串值: [root@ahui ~]# gawk 'BEGIN{FS="\n"; RS=""} {printf "%16s %s\n",$1, $4}' data2 Riley Mullen (312)555-2378 Frank Williams (312)234-3253 Haley Snell (312)123-2374 通过添加一个值为16的修饰符,我们强制第一个字符串的输出采用16位字符。默认情况下,printf命令使用右对齐来将数据放到格式化空间中。要改成左对齐,只要给修饰符加一个减号就可以了。 [root@ahui ~]# gawk 'BEGIN{FS="\n"; RS=""} {printf "%-16s %s\n", $1, $4}' data2 Riley Mullen (312)555-2378 Frank Williams (312)234-3253 Haley Snell (312)123-2374
(7)自定义函数 函数名必须能够唯一标识函数 2)使用自定义函数 在定义函数时,它必须出现在所有代码块之前(包括BEGIN代码块)。 [root@ahui ~]# gawk ' > function myprint() > { > printf "%-16s - %s\n", $1, $4 > } > BEGIN{FS="\n"; RS=""} > { > myprint() > }' data2 Riley Mullen - (312)555-2378 Frank Williams - (312)234-3253 Haley Snell - (312)123-2374 3)创建函数库 首先创建一个存储所有gawk函数的文件 [root@ahui ~]# cat funclib function myprint() { printf "%-16s - %s\n", $1, $4 } function myrand() { return int(limit * rand()) } function printthird() { printf $3 } 注意:不能将-f命令行参数和内联gawk脚本放到一起使用,不过可以在同一个命令行中使用多个-f参数 [root@ahui ~]# cat script4 BEGIN{ FS="\n"; RS="" } { myprint() } [root@ahui ~]# gawk -f funclib -f script4 data2 Riley Mullen - (312)555-2378 Frank Williams - (312)234-3253 Haley Snell - (312)123-2374(责任编辑:好模板) |