目录
85. 使用 printf 格式化输出
86. awk 内置数值函数
87. 随机数生成器
88. 常用字符串函数
89. GAWK/NAWK 的字符串函数
90. GAWK 字符串函数
91.处理参数(ARGC,ARGV,ARGIND)
92. OFMT
93. GAWK 内置的环境变量
94. pgawk – awk 运行分析器
95. 位操作
96.用户自定义函数
97. 使输出摆脱语言依赖(国际化)
98. 双向管道
99. 系统函数
100. 时间函数
101. getline 命令
85. 使用 printf 格式化输出
printf 可以非常灵活、简单地以你期望的格式输出结果。
语法 :
printf “print format”, variable1,variable2,etc. printf 中的特殊字符
printf 中可以使用下面的特殊字符
使用换行符把 Line1 和 Line2 打印在单独的行里:
$ awk 'BEGIN {printf "Line 1\nLine 2\n"}'
Line 1
Line 2
以制表符分隔字段,Field 1 后面有两个制表符:
$ awk 'BEGIN { printf "Field 1\t\tField 2\tField 3\tField 4\n"}'
Field 1 Field 2 Field 3 Field 4
每个字段后面使用垂直制表符:
$ awk 'BEGIN { printf "Field 1\vField 2\vField 3\vField 4\n" }'
Field 1
Field 2
Field 3
Field 4
下面的例子中,除了第 4 个字段外,每个字段后面使用退格符,这会擦除前三个字段最后的
数字。如 ”Field 1” 会被显示为 ”Field”, 因为最后一个字符被退格符擦除了。然而 ”Field 4” 会照旧
输出,因为它后面没有使用 \b.
$ awk 'BEGIN { printf "Field 1\bField 2\bField 3\bField 4\n" }'
Field Field Field Field 4
下面的例子,打印每个字段后,执行一个“回车”,在当前打印的字段的基础上,打印下一
个字段。这就意味着,最后只能看到 ”Field 4”, 因为其他的字段都被覆盖掉了 .
$ awk 'BEGIN { printf "Field 1\rField 2\rField 3\rField 4\n" }'
Field 4
使用 OFS,ORS
当使用 print( 不是 printf) 打印多个以逗号分隔的字段时, awk 默认会使用内置变量 OFS 和 ORS
处理输出。
下面例子展示 OFS 和 ORS 对单个 print 的影响 :
$ cat print.awk
BEGIN {
FS=",";
OFS=":"; ORS="\n--\n";
}
{
print $2,$3
}
$ awk -f print.awk items.txt
HD Camcorder:Video
--
Refrigerator:Appliance
--
MP3 Player:Audio
--
Tennis Racket:Sports
--
Laser Printer:Office
--
Printf 不受 OFS,ORS 影响
printf 不会使用 OFS 和 ORS ,它只根据 ”format” 里面的格式打印数据,如下所示 :
$ cat printf1.awk
BEGIN {
FS=",";
OFS=":";
ORS="\n--\n";
}
{
printf "%s^^%s\n",$2,$3
}
$ awk -f printf1.awk items.txt
HD Camcorder^^Video
Refrigerator^^Appliance
MP3 Player^^Audio
Tennis Racket^^Sports
Laser Printer^^Office
printf 格式化字符
下面展示各个格式化字符的基本用法 :
$ cat printf-format.awk
BEGIN {
printf "s--> %s\n", "String"
printf "c--> %c\n", "String"
printf "s--> %s\n", 101.23
printf "d--> %d\n", 101,23
printf "e--> %e\n", 101,23
printf "f--> %f\n", 101,23
printf "g--> %g\n", 101,23
printf "o--> %o\n", 0x8
printf "x--> %x\n", 16
printf "percentage--> %%\n", 17
}
$ awk -f printf-format.awk
s--> String
c--> S
s--> 101.23
d--> 101
e--> 1.010000e+02
f--> 101.000000
g--> 101
o--> 10
x--> 10
percentage--> %
指定打印列的宽度
要指定打印列的宽度,必须在 % 和格式化字符之间设置一个数字。该数字代表输出列的最小
宽度,如果字符串的宽度比该数字小,会在输出列左侧加上空格以凑足该宽度。
下面例子演示如何指定输出列宽度:
$ cat printf-width.awk
BEGIN {
FS=","
printf "%3s\t%10s\t%10s\t%5s\t%3s\n", "Num","Description","Type","Price","Qty"
printf "------------------------------------------------------------------\n"
}
{
printf "%3d\t%10s\t%10s\t%g\t%d\n", $1,$2,$3,$4,$5
}$ awk -f printf-width.awk items.txt
Num Description Type Price Qty
------------------------------------------------------------------
101 HD Camcorder Video 210 10
102 Refrigerator Appliance 850 2
103 MP3 Player Audio 270 15
104 Tennis Racket Sports 190 20
105 Laser Printer Office 475 5
注意,即是我们指定了输出列的宽度,输出结果仍然没有对齐。因为我们指定的是最小宽度,
而不是绝对宽度;如果字符串长度超过了指定的宽度,整个字符仍然会打印出来。所以,要
在到底打印多少宽度的字符上下点功夫。
如果想在字符串超出指定宽度时,仍然以指定的宽度把字符串打印出来,可以使用 substr
函数 ( 或者 ) 在指定宽度的数字前面加一个小数点 ( 稍后详述 ) 。
上面例子中,第二个字段的长度超过了 10 个字符,并不是我们期望的结果。
在左边补空格,把字符串”Good”打印成 6 个字符:
$ awk 'BEGIN { printf "%6s\n","Good" }'
Good
指定宽度为 6,但仍然输出所有字符:
$ awk 'BEGIN { printf "%6s\n", "Good Boy!" }'
Good Boy!
打印指定宽度 ( 左对齐 )
当字符串长度小于指定宽度时,如果要让它靠左对齐(右边补空格),那么要在%和格式化字符
之间加上一个减号(-)。
“%6s”是右对齐:
$ awk 'BEGIN { printf "|%6s|\n", "Good" }'
| Good|
“%-6s” 是左对齐 :
$ awk 'BEGIN { printf "|%-6s|\n", "Good" }'
|Good |
打印美元标识
如果要在价钱之前加上美元符号,只需在格式化字符串之前(%之前)加上$即可:
$ cat printf-width2.awk
BEGIN {
FS=","
printf "%3s\t%10s\t%10s\t%5s\t%3s\n", "Num","Description","Type","Price","Qty"
printf "------------------------------------------------------------------\n"}
{
printf "%3d\t%10s\t%10s\t$%-.2f\t%d\n", $1,$2,$3,$4,$5
}
$ awk -f printf-width2.awk items.txt
Num Description Type Price Qty
------------------------------------------------------------------
101 HD Camcorder Video $210.00 10
102 Refrigerator Appliance $850.00 2
103 MP3 Player Audio $270.00 15
104 Tennis Racket Sports $190.00 20
105 Laser Printer Office $475.00 5
字符串长度不足时补 0
默认情况下,右对齐时左边会补空格
$ awk 'BEGIN { printf "|%5s|\n", "100" }'
| 100|
为了在右对齐是,左边补 0 ( 而不是空格 ), 在指定宽度的数字前面加一个 0 ,即使用 ”%05s” 代
替 ”%5s”
$ awk 'BEGIN { printf "|%05s|\n", "100" }'
|00100|
下面的例子中,在打印的商品数量之前补 0 :
$ vim printf-width3.awk
BEGIN {
FS=","
printf "%-3s\t%-10s\t%-10s\t%-5s\t%-3s\n", "Num","Description","Type","Price","Qty"
printf "---------------------------------------------------------------------\n"
}
{
printf "%-3d\t%-10s\t%-10s\t$%-.2f\t%03d\n", $1,$2,$3,$4,$5
}
$ awk -f printf-width3.awk items.txt
Num Description Type Price Qty
---------------------------------------------------------------------
101 HD Camcorder Video $210.00 010
102 Refrigerator Appliance $850.00 002
103 MP3 Player Audio $270.00 015
104 Tennis Racket Sports $190.00 020
105 Laser Printer Office $475.00 005
以绝对宽度打印字符串
通过之前的例子可以知道,如果字符串长度超过指定的宽度,字符串仍然会整个被打印出来。
$ awk 'BEGIN { printf "%6s\n", "Good Boy!" }'
Good Boy!
如果要最多打印 6 个字符,要在指定宽度的数字前面加一个小数点,即使用 ”%.6s” 代替 ”%6s”,
这样即使字符串比指定宽度长,也只打印字符串中的前 6 个字符。
$ awk 'BEGIN { printf "%.6s\n", "Good Boy!" }'
Good B
这个例子并非适用于所有版本的 awk ,在 GAWK 3.1.5 上可以,但在 GAWK 3.1.7 上则不行。
当然,以绝对宽度打印字符串,最可取的方法是使用 substr 函数
$ awk 'BEGIN { printf "%6s\n", substr("Good Boy!",1,6) }'
Good B
控制精度
数字前面的点,用来指定其数值精度。
下面的例子说明如何控制精度,展示了使用 .1 和 .4 时数值 ”101.23” 的精度 ( 使用的格式化
字符有 d,e,f 和 g)
$ cat dot.awk
BEGIN {
print "---------- Using .1 -----------"
printf ".1d--> %.1d\n", 101.23
printf ".1e--> %.1e\n", 101.23
printf ".1f--> %.1f\n", 101.23
printf ".1g--> %.1g\n", 101.23
print "---------- Using .4 -----------"
printf ".4d--> %.4d\n", 101.23
printf ".4e--> %.4e\n", 101.23
printf ".4f--> %.4f\n", 101.23
printf ".4g--> %.4g\n", 101.23
}
$ awk -f dot.awk
---------- Using .1 -----------
.1d--> 101
.1e--> 1.0e+02
.1f--> 101.2
.1g--> 1e+02
---------- Using .4 -----------
.4d--> 0101
.4e--> 1.0123e+02
.4f--> 101.2300
.4g--> 101.2
把结果重定向到文件
Awk 中可以吧 print 语句打印的内容重定向到指定的文件中。下面的例子中,第一个 print
语句使用 ”>report.txt” 创建 report.txt 文件并把内容保存到该文件中。随后的所有 print 语句
都使用 ”>>report.txt”, 把内容追加到已存在的 report.txt 文件中。
$ cat printf-width4.awk
BEGIN {
FS=","
printf "%-3s\t%-10s\t%-10s\t%-5s\t%-3s\n", "Num","Description","Type","Price","Qty" >
"report.txt"
printf "---------------------------------------------------------------------\n" >> "report.txt"
}
{
if($5 > 10)
printf "%-3d\t%-10s\t%-10s\t$%-.2f\t%03d\n", $1,$2,$3,$4,$5 >> "report.txt"
}
~
$ awk -f printf-width4.awk items.txt
$ cat report.txt
Num Description Type Price Qty
---------------------------------------------------------------------
103 MP3 Player Audio $270.00 015
104 Tennis Racket Sports $190.00 020
另一中方法是不在 print 语句中使用 ”>” 或 ”>>” ,而是在执行 awk 脚本时使用重定向
$ vim printf-width5.awk
BEGIN {
FS=","
printf "%-3s\t%-10s\t%-10s\t%-5s\t%-3s\n", "Num","Description","Type","Price","Qty"
printf "---------------------------------------------------------------------\n"
}
{
if($5 > 10)
printf "%-3d\t%-10s\t%-10s\t$%-.2f\t%03d\n", $1,$2,$3,$4,$5
}
$ awk -f printf-width5.awk items.txt >report.txt
$ cat report.txt
Num Description Type Price Qty
---------------------------------------------------------------------
103 MP3 Player Audio $270.00 015
104 Tennis Racket Sports $190.00 020
86. awk 内置数值函数
Awk 有很多内置的数值、字符串、输入输出函数,下面介绍其中的一部分。
int(n) 函数
int() 函数返回给定参数的整数部分值。 n 可以是整数或浮点数,如果使用整数做参数,返回
值即是它本身,如果指定浮点数,小数部分会被截断。
int 函数示例 :
$ awk 'BEGIN {
> print int(3.534);
> print int(4);
> print int(-5.223);
> print int(-5);
> }'
输出结果为 :
3
4
-5
-5
log(n) 函数 :
log(n) 函数返回给定参数的自然对数,参数 n 必须是正数,否则会抛出错误
log 函数示例:
$ awk 'BEGIN {
print log(12);
print log(0);
print log(1);
print log(-1);
> }'
2.48491
-inf
0
awk: cmd. line:4: warning: log: received negative argument -1
nan
可以看到,该例子中 log(0) 的值是无穷大,显示为 -inf, log(-1) 抛出了错误 ( 非数字 )
注意:你可以同时会看到 log(-1) 抛出如下错误信息 awk: cmd. line:4: warning: log: received
negative argument -1
sqrt(n) 函数
sqrt 函数返回指定整数的正平方根,该函数参数也必须是整数,如果传递负数将会报错。
sqrt 函数示例:
$ awk 'BEGIN { print sqrt(3.5) }'
1.87083
$ awk 'BEGIN {
> print sqrt(16);
> print sqrt(0);
> print sqrt(-12);
> } '
4
0
awk: cmd. line:4: warning: sqrt: called with negative argument -12
nan
exp(n) 函数
exp 函数返回 e 的 n 次幂
exp 函数示例 :
$ awk 'BEGIN {
> print exp(123434346);
> print exp(0);
> print exp(-12);
> }'
awk: cmd. line:1: warning: exp: argument 1.23434e+08 is out of range
inf
1
6.14421e-06
这个例子中, exp(1234346) 返回的值是 inf, 因为这个值已经超出范围 ( 溢出 ) 了。
sin(n) 函数
sin(n) 返回 n 的正弦值 ,n 是弧度值
sin 函数示例 :
$ awk 'BEGIN {
> print sin(90);
> print sin(45);
> }'
0.893997
0.850904
cos(n) 函数
cos(n) 返回 n 的余弦值, n 是弧度值
cos 函数示例:
$ awk 'BEGIN {> print cos(90);
> print cos(45);
> }'
-0.448074
0.525322
atan2(m,n) 函数
该函数返回 m/n 的反正切值 ,m 和 n 是弧度值。
atan2 函数示例 :
$ awk 'BEGIN { print atan2(30,45) }'
0.588003
87. 随机数生成器
rand() 函数用于产生 0~1 之间的随机数,它只返回 0~1 之间的数,绝不会返回 0 或 1 。这些
数在 awk 运行时是随机的,但是在多次运行中,又是可预知的。
awk 使用一套算法产生随机数,因为这个算法是固定的,所以产生的数也有重复的。
下面的例子产生 1000 个 0 到 100 之间的随机数,并且演示了每个数是怎么产生的
产生 1000 个随机数 (0 到 100 之间 ):
$ cat rand.awk
BEGIN {
while(i<1000)
{
n = int(rand()*100);
rnd[n]++;
i++;
}
for(i=0;i<=100;i++)
{
print i,"Occured",rnd[i],"times";
}
}
$ awk -f rand.awk
0 Occured 11 times
1 Occured 8 times
2 Occured 9 times
3 Occured 15 times
4 Occured 16 times
5 Occured 5 times
6 Occured 8 times
7 Occured 9 times
8 Occured 7 times
9 Occured 7 times
10 Occured 11 times
11 Occured 7 times
12 Occured 10 times
13 Occured 9 times
14 Occured 6 times
15 Occured 18 times
16 Occured 10 times
17 Occured 10 times
18 Occured 9 times
19 Occured 8 times
20 Occured 11 times
21 Occured 13 times
22 Occured 10 times
23 Occured 9 times
24 Occured 15 times
25 Occured 8 times
26 Occured 3 times
27 Occured 17 times
28 Occured 9 times
29 Occured 13 times
30 Occured 11 times
31 Occured 9 times
32 Occured 12 times
33 Occured 12 times
34 Occured 9 times
35 Occured 6 times
36 Occured 13 times
37 Occured 15 times
38 Occured 6 times
39 Occured 9 times
40 Occured 7 times
41 Occured 8 times
42 Occured 6 times
43 Occured 8 times
44 Occured 10 times
45 Occured 7 times
46 Occured 10 times
47 Occured 8 times
48 Occured 16 times
49 Occured 12 times
50 Occured 6 times
51 Occured 15 times
52 Occured 6 times
53 Occured 12 times
54 Occured 8 times
55 Occured 13 times
56 Occured 6 times
57 Occured 16 times
58 Occured 5 times
59 Occured 7 times
60 Occured 11 times
61 Occured 12 times
62 Occured 14 times
63 Occured 11 times
64 Occured 9 times
65 Occured 6 times
66 Occured 7 times
67 Occured 10 times
68 Occured 8 times
69 Occured 12 times
70 Occured 13 times
71 Occured 9 times
72 Occured 10 times
73 Occured 11 times
74 Occured 7 times
75 Occured 13 times
76 Occured 13 times
77 Occured 10 times
78 Occured 5 times
79 Occured 12 times
80 Occured 17 times
81 Occured 8 times
82 Occured 7 times
83 Occured 10 times
84 Occured 12 times
85 Occured 12 times
86 Occured 11 times
87 Occured 14 times
88 Occured 4 times
89 Occured 8 times
90 Occured 15 times
91 Occured 10 times
92 Occured 15 times
93 Occured 8 times
94 Occured 11 times
95 Occured 5 times
96 Occured 12 times
97 Occured 11 times
98 Occured 7 times
99 Occured 11 times
100 Occured times
通过这个例子可以看出, rand() 函数产生的随机数有很高的重复率。
srand(n)函数
srand(n)函数使用给定的参数 n 作为种子来初始化随机数的产生过程。不论何时启动,awk
只会从 n 开始产生随机数,如果不指定参数 n,awk 默认使用当天的时间作为产生随机数的
种子。
产生 5 个从 5 到 50 的随机数 :
$ cat srand.awk
BEGIN {
#Initialize the sedd with 5.
srand(5);
#Totally I want to generate 5 numbers
total = 5;
#maximun number is 50
max = 50;
count = 0;
while(count < total)
{
rnd = int(rand()*max);
if( array[rnd] == 0 )
{
count++;
array[rnd]++;
}
}
for ( i=5;i<=max;i++)
{
if (array[i])
print i; }
}
$ awk -f srand.awk
14
16
23
33
35
该例中 :
首先使用 rand() 函数产生随机数,然后乘以期望的最大值,获得一个小于 50 的数
检测产生的数是否存在于数组中,如果不存在,增加数组的索引和循环数。本例中
产生 5 个数
最后在 for 循环中,从最小到最大一次打印每个索引对应的元素值。
88. 常用字符串函数
下面是一些可以在所有风格的 awk 上运行的常用字符串函数。
index 函数
index 函数用来获取给定字符串在输入字符串中的索引 ( 位置 ) 。
下面的例子中,字符串 ”Cali” 在字符串 ”CA is California” 中的位置是 7.
也可以用 index 来检测指定的字符串 ( 或者字符 ) 是否存在于输入字符串中。如果指定的字符
串没有出现,返回 0 ,就说明指定的字符串不存在,如下所示。
$ cat index.awk
BEGIN {
state="CA is California"
print "String CA starts at location",index(state,"CA");
print "String Cali starts at location",index(state,"Cali");
if(index(state,"NY")==0)
print "String NY is not found in:",state
}
$ awk -f index.awk
String CA starts at location 1
String Cali starts at location 7
String NY is not found in: CA is California
length 函数
length 函数返回字符串的长度,下面例子将打印 items.txt 文件中每行字符串的总数。
$ awk '{print length($0)}' items.txt
30
33
28
32
30
split 函数
语法 :
split(input-string,output-array,separator)
split 函数把字符串分割成单个数组元素,其接受如下参数:
input-string: 这个是需要被分割的字符串
output-array: 这个是分割后的字符串存放的数组
separator: 分割字符串的字段分隔符
为了演示这个例子,要对原先的 items-sold.txt 文件做小小的改动,使其包含不同的字段分
隔符,即使用冒号分隔商品编号和销售量,在销售量列表中,每个数字之间以逗号分隔。
为了统计某件特定商品的销售量,我们需要取出第 2 个字段 ( 以逗号分隔的销售量数字列表 ),
然后使用逗号作为分隔符,将其分隔并保存在一个数组中,然后使用循环遍历数组统计总和。
$ cat items-sold1.txt
101:2,10,5,8,10,12
102:0,1,4,3,0,2
103:10,6,11,20,5,13
104:2,3,4,0,6,5
105:10,2,5,7,12,6
$ cat split.awk
BEGIN {
FS=":"
}
{
split($2,quantity,",");
total=0;
for(x in quantity)
total=total+quantity[x];
print "Item",$1,":",total,"quantities sold";
}
$ awk -f split.awk items-sold1.txt
Item 101 : 47 quantities sold
Item 102 : 10 quantities sold
Item 103 : 65 quantities sold
Item 104 : 20 quantities sold
Item 105 : 42 quantities sold
substr 函数
语法:
substr(input-string,location,length)
substr 函数从字符串中ᨀ取指定的部分 ( 子串 ) ,上面语法中:
input-string: 包含子穿的字符串
location: 子串的开始位置
length: 从 location 开始起,出去的字符串的总长度。这个选项是可选的,如果不指
定长度,那么从 location 开始一直取到字符串的结尾
下面的例子从字符串的第 5 个字符开始,取到字符串结尾并打印出来。开始的 3 个字符是商
品编号,第 4 个字符时逗号。所以下面的例子会跳过商品编号,打印剩余的内容。
$ awk '{ print substr($0,5) }' items.txt
HD Camcorder,Video,210,10
Refrigerator,Appliance,850,2
MP3 Player,Audio,270,15
Tennis Racket,Sports,190,20
Laser Printer,Office,475,5
从第 2 个字段的第 1 个字符起,打印 5 个字符 :
$ awk -F"," '{ print substr($2,1,5) }' items.txt
HD Ca
Refri
MP3 P
Tenni
Laser
89. GAWK/NAWK 的字符串函数
下面这些函数只能在 GAWK 和 NAWK 中使用。
sub 函数:
语法:
sub(original-string,replacement-string,string-variable)
sub 代表 substitution( 替换 ) 的意思
original-string: 将被替换掉的字符串。也可以是一个正则表达式。
replacement-string: 用来替换的字符串
string-variable: 既是输入字符串,也是输出字符串。必须小心,一旦替换操作成功
执行,你将丢失该字符串原来的值。
下面的例子中:
original-string: 这里是一个正则表达式 C[Aa], 匹配 CA 或者 Ca
replacement-string: 如果匹配到 original-string ,则用 ”KA” 替换之
string-variable: 执行替换操作之前,该变量保存的是输入字符串 ( 替换前的字符串 ) ,
一旦执行替换操作,该变量保存的是输出字符串 ( 替换后的字符串 )
需要注意的是, sub 函数只替换第一次出现的 original-string 。
$ cat sub.awk
BEGIN {
state="CA is California"
sub("C[Aa]","KA",state);
print state;
}
$ awk -f sub.awk
KA is California
第 3 个参数 string-variable 是可选的,如果没有指定, awk 会使用 $0( 当前记录 ) 做为第 3 个参
数,如下所示。这个例子把头两个字符从 ”10” 替换为 ”20”, 所有,商品编号 101 变成了 201,102
变成了 202 ,依此类推。
$ awk '{ sub("10","20"); print $0 }' items.txt
201,HD Camcorder,Video,210,10
202,Refrigerator,Appliance,850,2
203,MP3 Player,Audio,270,15
204,Tennis Racket,Sports,190,20
205,Laser Printer,Office,475,5
如果替换操作执行成功, sub 函数返回 1 ,否则返回 0.
仅打印替换成功的记录 :
$ awk '{ if(sub("HD","High-Def")) print $0; }' items.txt
101,High-Def Camcorder,Video,210,10
gsub 函数
gsub 代表全局替换。和 sub 基本相同,只不过它把所有出现的 original-string 都替换为
replacement-string 。
下面例子中, ”CA” 和 ”Ca” 都将被替换成 ”KA”:
$ cat gsub.awk
BEGIN {
state="CA is California"
gsub("C[Aa]","KA",state);
print state;
}
$ awk -f gsub.awk
KA is KAlifornia
和 sub 函数相同,第 3 个参数也是可选的,如果没有指定,则使用 $0 作为第 3 个参数。
下面的例子把所有出现的 ”10” 都替换为 ”20” ,它不仅会替换商品编号,如果其他字段包含了
10 ,也会进行替换。
$ awk '{ gsub("10","20"); print $0 }' items.txt
201,HD Camcorder,Video,220,20 202,Refrigerator,Appliance,850,2
203,MP3 Player,Audio,270,15
204,Tennis Racket,Sports,190,20
205,Laser Printer,Office,475,5
match 函数和 RSTART,RLENGTH 变量
match 函数从输入字符串中检索给定的字符串 ( 或正则表达式 ) ,当检索到字符串时,返回一
个正数值。
语法:
match(input-string,search-string)
input-string: 这是需要被检索的字符串
search-string: 要检索的字符串,需要包含在 input-string 中,它可以是一个正则表达
式
下面例子在 state 字符串中检索 ”Cali”, 如果 Cali 出现,则打印一条检索成功的消息。
$ cat match.awk
BEGIN {
state="CA is California"
f(match(state,"Cali"))
{
print substr(state,RSTART,RLENGTH),"is present in:",state;
}
}
$ awk -f match.awk
Cali is present in: CA is California
match 函数设置了两个特殊变量,这个例子在调用 substr 函数时使用了它们用来打印检索成
功的消息。
RSTART – search-string 的开始位置
RLENGTH – search-string 的长度
90. GAWK 字符串函数
tolower 和 toupper 函数仅在 GAWK 中可以使用。正如函数名一样,这两个函数把给定的字
符串转换成小写或大写形式,如下所示:
$ awk '{ print tolower($0) }' items.txt
101,hd camcorder,video,210,10
102,refrigerator,appliance,850,2
103,mp3 player,audio,270,15
104,tennis racket,sports,190,20
105,laser printer,office,475,5
$ awk '{ print toupper($0) }' items.txt
101,HD CAMCORDER,VIDEO,210,10 102,REFRIGERATOR,APPLIANCE,850,2
103,MP3 PLAYER,AUDIO,270,15
104,TENNIS RACKET,SPORTS,190,20
105,LASER PRINTER,OFFICE,475,5
91.处理参数(ARGC,ARGV,ARGIND)
之前我们已经讨论过 awk 内置变量, FS,NFS,RS,NR,FILENAME,OFS 和 ORS ,这些变量在所有的
awk 版本 ( 包括 nawk 和 gawk) 上都可以使用。
本节中ᨀ到的环境变量仅仅适用于 nawk 和 gawk
可以使用 ARGC 和 ARGV 从命令行传递一些参数给 awk 脚本
ARGC 保存着传递给 awk 脚本的所有参数的个数
ARGV 是一个数组,保存着传递给 awk 脚本的所有参数,其索引范围从 0 到 ARGC
当传递 5 个参数是, ARGC 的值为 6
ARGV[0] 的值永远是 awk
下面的例子 arguments.awk 演示 ARGC 和 ARGV 的作用 :
$ cat arguments.awk
BEGIN {
print "ARGC=",ARGC
for(i=0;i<ARGC;i++)
print ARGV[i]
}
$ awk -f arguments.awk
arg1 arg2 arg3 arg4 arg5
ARGC= 6
awk
arg1
arg2
arg3
arg4
arg5
在下面的例子中 :
我们以 ”— 参数名 参数值 ” 的格式给 awk 脚本传递一些参数
awk 脚本获取传递元素的内容和数量作为参数
如果把 ”—item 104 –qty 25” 作为参数传递给 awk 脚本, awk 会把商品 104 的数量设
置为 25
如果把 ”—item 105 –qty 3” 作为参数传递给 awk 脚本, awk 会把商品 105 的数量设
置为 3
$ cat argc-argv.awk
BEGIN {
FS=","; OFS=",";
for(i=0;i<ARGC;i++)
{
if(ARGV[i] == "--item")
{
itemnumber=ARGV[i+1];
delete ARGV[i]
i++;
delete ARGV[i]
}
else if (ARGV[i]=="--qty")
{
quantity=ARGV[i+1]
delete ARGV[i]
i++;
delete ARGV[i]
}
}
}
{
if ($1==itemnumber)
print $1,$2,$3,$4,quantity
else
print $0
}
$ awk -f argc-argv.awk --item 104 --qty 25 items.txt
101,HD Camcorder,Video,210,10
102,Refrigerator,Appliance,850,2
103,MP3 Player,Audio,270,15
104,Tennis Racket,Sports,190,25
105,Laser Printer,Office,475,5
在 gawk 中,当前处理的文件被存放在数组 ARGV 中,该数组在 body 区域被访问。 ARGIND
是 ARGV 的一个索引,其对应的值是当前正在处理的文件名。
当 awk 脚本仅处理一个文件时, ARGIND 的值是 1 , ARGV[ARGIND] 会返回当前正在处理的文
件名。
下面的例子只有 body 区域,打印 ARGIND 的值以及 ARGV[ARGIND]
$ cat argind.awk
{
print "ARGIND:",ARGIND print "Current file:",ARGV[ARGIND]
}
调用这个脚本时,可以传递两个文件给它,没处理一行记录就会打印两条数据。这个例子意
在让你搞清楚 ARGIND 和 ARGV[ARGIND] 的值是怎么存储的。
$ awk -f argind.awk items.txt items-sold1.txt
ARGIND: 1
Current file: items.txt
ARGIND: 1
Current file: items.txt
ARGIND: 1
Current file: items.txt
ARGIND: 1
Current file: items.txt
ARGIND: 1
Current file: items.txt
ARGIND: 2
Current file: items-sold1.txt
ARGIND: 2
Current file: items-sold1.txt
ARGIND: 2
Current file: items-sold1.txt
ARGIND: 2
Current file: items-sold1.txt
ARGIND: 2
Current file: items-sold1.txt
92. OFMT
内置变量 OFMT 仅适用于 NAWK 和 GAWK 。
当一个数值被转换成字符串并打印时, awk 适用 OFMT 格式来决定如何打印这些值。 OFMT
默认值是 ”%.6g”, 包括小数点两边的数字,它打印一共 6 个长度的字符。
在使用 g 时,必须数清楚小数点两边的数字位数,如 ”%.4g” 表示包括小数点两侧,一共打印
4 个字符。
在使用 f 时,只需要数清楚小数点后面的数字位数,如 ”%.4f” 表示小数点后面会打印 4 个字
符。在此不必关系小数点左边有多少个字符。
下面的脚本 ofmt.awk 演示在使用不同的 OFMT 值 (g 和 f) 时,如何打印字符串
$ cat ofmt.awk
BEGIN {
total=143.123456789; print "--- using g ---"
print "Default OFMT:",total;
OFMT="%.3g"
print "%.3g OFMT:",total;
OFMT="%.4g"
print "%.4g OFMT:",total;
OFMT="%.5g"
print "%.5g OFMT:",total;
OFMT="%.6g"
print "%.6g OFMT:",total;
print "--- using f ---"
OFMT="%.0f";
print "%.0f OFMT:",total;
OFMT="%.1f";
print "%.1f OFMT:",total;
OFMT="%.2f";
print "%.2f OFMT:",total;
OFMT="%.3f";
print "%.3f OFMT:",total;
}
$ awk -f ofmt.awk
--- using g ---
Default OFMT: 143.123
%.3g OFMT: 143
%.4g OFMT: 143.1
%.5g OFMT: 143.12
%.6g OFMT: 143.123
--- using f ---
%.0f OFMT: 143
%.1f OFMT: 143.1
%.2f OFMT: 143.12
%.3f OFMT: 143.123
93. GAWK 内置的环境变量
本节讨论的内置变量仅适用于 GAWK 。
ENVIRON
如果能在 awk 脚本中访问 shell 环境变量会十分有用。 ENVIRON 是一个包含所有 shell 环境变
量的数组,其索引就是环境变量的名称。
如元素 ENVIRON[“PATH”] 的值是环境变量 PATH 的值。 下面的例子打印所有环境变量名称和值。
$ cat environ.awk
BEGIN {
OFS="="
for(x in ENVIRON)
print x,ENVIRON[x]
}
$ awk -f environ.awk
MAIL=/var/mail/root
CPU=x86_64
XDG_CONFIG_DIRS=/etc/xdg
LC_CTYPE=en_US.UTF-8
INPUTRC=/etc/inputrc
HOST=mkey
PWD=/root
FROM_HEADER=
………..
IGNORECASE
默认情况下, IGNORECASE 的值是 0 ,所有 awk 区分大小写。
当把 IGNORECASE 的值设置为 1 时, awk 则不区分大小写,这在使用正则表达式和比较字符
串时很有效率。
下面的例子不会打印任何内容,因为它想用匹配小写的”video”,但 items.txt 中包含的却是
大写的”Video”.
awk '/video/ {print}' items.txt
然而,当把 IGNORECASE 设置为 1 时,就能匹配并打印包含”Video”的行,因为现在 awk 不
区分大小写。
$ awk 'BEGIN{IGNORECASE=1} /video/{print}' items.txt
101,HD Camcorder,Video,210,10
下面的例子同时支持字符串比较和正则表达式。
$ cat ignorecase.awk
BEGIN {
FS=",";
IGNORECASE=1;
}
{
if ($3 == "video") print $0;
if ($2 ~ "TENNIS") print $0;
}
$ awk -f ignorecase.awk items.txt
101,HD Camcorder,Video,210,10
104,Tennis Racket,Sports,190,20
ERRNO
当执行 I/O 操作 ( 比如 getline) 出错时,变量 ERRNO 会保存错误信息。
下面的例子试图用 getline 读取一个不存在的文件,此时 ERRNO 的内容将会是 ”No such file or
directory” 。
$ cat errno.awk
{
print $0;
x = getline < "dummy-file.txt"
if ( x == -1 )
print ERRNO
else
print $0
}
$ awk -f errno.awk items.txt
101,HD Camcorder,Video,210,10
No such file or directory
102,Refrigerator,Appliance,850,2
No such file or directory
103,MP3 Player,Audio,270,15
No such file or directory
104,Tennis Racket,Sports,190,20
No such file or directory
105,Laser Printer,Office,475,5
No such file or directory
94. pgawk – awk 运行分析器
pgawk 程序用来生成 awk 执行的结果报告。用 pgawk 可以看到 akw 每次执行了多少条语句 ( 以
及用户自定义的函数 ) 。
首先建立下面的 awk 脚本作为样本,以供 pgawk 执行,然后分析其结果。
$ cat profiler.awk
BEGIN {
FS=",";
print "Report Generate On:",strftime("%a %b %d %H:%M:%S %Z %Y",systime());
}
{
if ( $5 <= 5 )
print "Buy More: Order",$2,"immediately!"
else print "Sell More: Give discount on",$2,"immediately!"
}
END {
print "----------"
}
接下来使用 pgawk(不是直接调用 awk)来执行该样本脚本。
$ pgawk -f profier.awk items.txt
Report Generate On: Tue Apr 09 15:56:26 CST 2013
Sell More: Give discount on HD Camcorder immediately!
Buy More: Order Refrigerator immediately!
Sell More: Give discount on MP3 Player immediately!
Sell More: Give discount on Tennis Racket immediately!
Buy More: Order Laser Printer immediately!
----------
pgawk 默认会创建输出文件 profier.out( 或者 awkprof.out) ,使用 —profier 选项可以指定输出
文件,如下所示。
$ pgawk --profile=myprofiler.out -f profier.awk items.txt
查看默认的输出文件 awkprof.out 来弄清楚每条单独的 awk 语句的执行次数。
$ cat awkprof.out
# gawk profile, created Tue Apr 9 15:56:26 2013
# BEGIN block(s)
BEGIN {
1 FS = ","
1 print "Report Generate On:", strftime("%a %b %d %H:%M:%S %Z %Y", systime())
}
# Rule(s)
5 {
5 if ($5 <= 5) { # 2
2 print "Buy More: Order", $2, "immediately!"
3 } else {
3 print "Sell More: Give discount on", $2, "immediately!"
}
}
# END block(s)
END { 1 print "----------"
}
查看 awkprof.out 文件时,务必牢记 :
左侧一列有一个数字,标识着该 awk 语句执行的次数。如 BEGIN 区域里的 print 语
句仅执行了一次 (duh!) 。而 while 循环执行了 5 次。
对于任意一个条件判断语句,左边有一个数字,括号右边也有一个数字。左边数字
代表该判断语句执行了多少次,右边数字代表判断语句为 true 的次数。上面的例
子中,由 (# 2) 可以判定, if 语句执行了 5 次,但只有 2 次为 true 。
95. 位操作
和 C 语言类似, awk 也可以进行位操作。在日常工作中用不到,但可以说明你可以利用位操
作来做什么。
下面表格列出了十进制数字及其二进制形式
AND ( 按位与 )
要使 AND 结果为 1 ,两个操作数都必须为 1.
0 and 0 = 0
0 and 1 = 0
1 and 0 = 0
1 and 1 =1
例如,在十进制数 15 和 25 上执行 AND 操作,结果是二进制的 01001 ,也就是十进制的 9.
15 = 01111
25 = 11001
15 and 25 = 01001
OR( 按位或 )
要使 OR 结果为 1 ,任意一个操作数为 1 即可
0 or 0 = 0
0 or 1 = 1
1 or 0 = 1
1 or 1 = 1 例如,在十进制数 15 和 25 上执行 OR 操作,结果是二进制的 11111 ,也就是十进制的 31.
15 = 01111
25 = 11001
15 and 25 = 11111
XOR( 按位异或 )
要使 XOR 结果为 1 ,必须只有一个操作数为 1
0 xor 0 = 0
0 xor 1 = 1
1 xor 0 = 1
1 xor 1 = 0
例如,在十进制数 15 和 25 上执行 XOR 操作,结果是二进制的 10110 ,也就是十进制的 22.
15 = 01111
25 = 11001
15 and 25 = 10110
complement( 取反码 )
反码把 0 变成 1 ,把 1 变成 0
如,给 15 取反码。
15 = 01111
15 compl= 10000
Left Shift( 左移 )
该函数把操作数向左位移,可以指定位移多少次 , 位移后右边补 0 。
例如把十进制数 15 向左位移 ( 移两次 ), 结果将是二进制的 111100 ,即十进制的 60.
15 = 1111
lshift twice = 111100
Right Shift( 右移 )
该函数把操作数向右位移,可以指定位移多少次 , 位移后左边补 0 。
例如把十进制数 15 向右位移 ( 移两次 ), 结果将是二进制的 0011 ,即十进制的 3.
15 = 1111
lshift twice = 0011
awk 位移函数示例
$ cat bits.awk
BEGIN {
number1=15
number2=25
print "AND: " and(number1,number2);
print "OR: " or(number1,number2);
print "XOR: " xor(number1,number2);
print "LSHIFT: " lshift(number1,2); print "RSHIFT: " rshift(number1,2);
}
$ awk -f bits.awk
AND: 9
OR: 31
XOR: 22
LSHIFT: 60
RSHIFT: 3
96.用户自定义函数
awk 运行用户自定义函数,这在编写大量代码同时又要多次重复执行其中某些片段时特别有
用,这些片段就适合定义成函数。
语法:
function fn-name(parameters)
{
function-body
}
其中 :
fn-name: 函数名,和 awk 变量名一样,用户定义的函数名应该以字母开头,后续
字符可以数字、字母或下划线,关键字不能用做函数名
parameters: 多个参数要使用逗号分开,也可以定义一个没有参数的函数
function-body: 一条或多条 awk 语句
如果你在 awk 中已经使用了某个名字作为变量名,那么它就不能再用来作函数名。
下面的例子创建了一个简单的用户自定义函数—— discount, 它返回商品打折后的价钱,如
discount(10) 返回打九折后的价钱。
对于任意一种商品,如果数量不大于 10 ,则打九折,否则打 5 折。
$ cat function.awk
BEGIN {
FS=","
OFS=","
}
{
if ($5 <= 10)
print $1,$2,$3,discount(10),$5
else
print $1,$2,$3,discount(50),$5
}function discount(percentage)
{
return $4 - ($4*percentage/100);
}
$ awk -f function.awk items.txt
101,HD Camcorder,Video,189,10
102,Refrigerator,Appliance,765,2
103,MP3 Player,Audio,135,15
104,Tennis Racket,Sports,95,20
105,Laser Printer,Office,427.5,5
自定义函数另外一个作用是打印 debug 信息。
下面是一个简单的 mydebug 函数 :
$cat function-debug.awk
{
i=2; total=0;
while (i <= NF ) {
mydebug("quantity is "$i);
total = total + $i;
i++;
}
print "Item",$1,":",total,"quantities sold";
}
function mydebug( message )
{
printf("DEBUG[%d]>%s\n",NR,message);
}
$ awk -f function-debug.awk items-sold.txt
DEBUG[1]>quantity is 2
DEBUG[1]>quantity is 10
DEBUG[1]>quantity is 5
DEBUG[1]>quantity is 8
DEBUG[1]>quantity is 10
DEBUG[1]>quantity is 12
Item 101 : 47 quantities sold
DEBUG[2]>quantity is 0
DEBUG[2]>quantity is 1
DEBUG[2]>quantity is 4
DEBUG[2]>quantity is 3
DEBUG[2]>quantity is 0 DEBUG[2]>quantity is 2
Item 102 : 10 quantities sold
DEBUG[3]>quantity is 10
DEBUG[3]>quantity is 6
DEBUG[3]>quantity is 11
DEBUG[3]>quantity is 20
DEBUG[3]>quantity is 5
DEBUG[3]>quantity is 13
Item 103 : 65 quantities sold
DEBUG[4]>quantity is 2
DEBUG[4]>quantity is 3
DEBUG[4]>quantity is 4
DEBUG[4]>quantity is 0
DEBUG[4]>quantity is 6
DEBUG[4]>quantity is 5
Item 104 : 20 quantities sold
DEBUG[5]>quantity is 10
DEBUG[5]>quantity is 2
DEBUG[5]>quantity is 5
DEBUG[5]>quantity is 7
DEBUG[5]>quantity is 12
DEBUG[5]>quantity is 6
Item 105 : 42 quantities sold
97. 使输出摆脱语言依赖(国际化)
在使用 awk 脚本打时,可能需要用 print 打印指定的 header 和 footer 信息。你或许会用英文
把 header 和 footer 写成固定的内容。但在其他语言中,你希望它会打印什么信息?最终你
可能会把脚本复制过去,然后修改要打印的固定信息,来适应当前的语言环境。
有个更容易的方法来实现这个目的——国际化,这样使用同一个脚本,仅需要在运行脚本时
修改那些固定的输出信息即可。
在运行大型程序,而出于某些原因你要频繁地修改那些固定的输出信息时;或者希望用户能
自行修改要输出的内容时,这个方法也很有用。
下面的例子演示了在 awk 中实现国际化的关键 4 步。
步骤 1 – 建立文本域
创建一个文本域文件,并把它和 awk 要搜寻的目录绑定。下面以当前目录为例。
$ cat iteminfo.awk
BEGIN {
FS=","
TEXTDOMAIN = "item" bindtextdomain(".")
print _"START_TIME:" strftime("%a %b %d %H:%M:%S %Z %Y",systime());
printf "%-3s\t",_"Num";
printf "%-10s\t",_"Description"
printf "%-10s\t",_"Type"
printf "%-5s\t",_"Price"
printf "%-3s\n",_"Qty"
printf _"---------------------------------------------------\n"
}
{
printf "%-3d\t%-10s\t%-10s\t%-.2f\t%03d\n",$1,$2,$3,$4,$5
}
注意:这个例子中,前面带 ”_” 的字符串均可以自行定义。字符串前面的 _( 下划线 ) 不会影响
字符串内容的打印,即它和下面的输出完全相同。
$ awk -f iteminfo.awk items.txt
START_TIME:Thu Apr 11 11:37:40 CST 2013
Num Description Type Price Qty
---------------------------------------------------
101 HD Camcorder Video 210.00 010
102 Refrigerator Appliance 850.00 002
103 MP3 Player Audio 270.00 015
104 Tennis Racket Sports 190.00 020
105 Laser Printer Office 475.00 005
步骤 2 :生成 .po 文件
建立如下可移植对象文件 ( 扩展名 .po) ,注意,除了使用 —gen-po 之外,也可以使用 ”-W gen-po”
$ gawk --gen-po -f iteminfo.awk > iteminfo.po
$ cat iteminfo.po
#: iteminfo.awk:5
msgid "START_TIME:"
msgstr ""
#: iteminfo.awk:6
msgid "Num"
msgstr ""
#: iteminfo.awk:7
msgid "Description"
msgstr ""
#: iteminfo.awk:8
msgid "Type" msgstr ""
#: iteminfo.awk:9
msgid "Price"
msgstr ""
#: iteminfo.awk:10
msgid "Qty"
msgstr ""
#: iteminfo.awk:11
msgid "---------------------------------------------------\n"
""
msgstr ""
现在修改该文件中相应的内容。例如要显示 ”Report Generated on:”( 替换原来
的 ”START_TIME”) ,那么修改 iteminfo.po 文件,把 START_TIME 右下方的 msgstr 修改为“ Reprot
Genterated On:”
$ cat iteminfo.po
#: iteminfo.awk:5
msgid "START_TIME:"
msgstr "Report Generated On:"
ᨀ示:这个例子中,其余的 msgstr 字符串均被置空。
步骤 3 :生成消息对象
使用 msgfmt 命令 ( 从可移植对象文件生成 ) 生成消息对象文件。
如果 iteminfo.po 文件中所有的 msgstr 都是空,那么将不会生成任何消息对象文件,如:
$ msgfmt –v iteminfo.po
0 translated messages, 7 untranslated messages.
我们已经创建了一条消息,所以会生成 messages.mo 文件。
$ msgfmt -v iteminfo.po
1 translated message, 6 untranslated messages.
$ ls -l messages.mo
-rw-r--r-- 1 root root 89 Apr 11 12:11 messages.mo
把 message.mo 文件复制到消息目录下,消息目录应该在当前目录下创建
$ mkdir -p en_US/LC_MESSAGES
$ mv messages.mo en_US/LC_MESSAGES/item.mo
注意:目标文件的名字要和初始 awk 文件中的 TEXTDOAMIN 后面的值相同。之前的 awk 文
件中 TEXTDOMAIN=”item” 。
步骤 4 :核证消息 现在可以看到, awk 将不再显示 ”START_TIME”, 而是转换为 ”Report Generated On:” 然后打印出
来:
$ gawk -f iteminfo.awk items.txt
Reprot Generated On:Thu Apr 11 12:16:19 CST 2013
Num Description Type Price Qty
---------------------------------------------------
101 HD Camcorder Video 210.00 010
102 Refrigerator Appliance 850.00 002
103 MP3 Player Audio 270.00 015
104 Tennis Racket Sports 190.00 020
105 Laser Printer Office 475.00 005
98. 双向管道
awk 可以使用 ”|&” 和外部进程通信,这个过程是双向的。
下面的例子把关键字 ”Awk” 替换为 ”Sed and Awk” 。
$ echo "Awk is great" | sed 's/Awk/Sed and Awk/'
Sed and Awk is great
下面的例子使用 ”|&” 了模拟实现上面的例子,以说明 awk 是如何双向管道的。
$ cat two-way.awk
BEGIN {
command = "sed 's/Awk/Sed and Awk/'"
print "Awk is Great!" |& command
close(command,"to");
command |& getline tmp
print tmp;
close(command);
}
$ awk -f two-way.awk
Sed and Awk is Great!
这个例子中:
command = “sed ‘s/Awk/Sed and Awk/’” – 这是要和 awk 双向管道对接的命令。它是
一个简单的 sed 替换命令,把 ”Awk” 替换为 ”Sed and Awk” 。
print “Awk is Great!” |& command – command 的输入,即 sed 替换命令的输入是 ”Awk
is Great!” 。 ”|&” 表示这里是双向管道。 ”|&” 右边命令的输入来自左边命令的输出。
close(command,”to”) – 一旦命令执行完成,应该关闭 ”to” 进程。
command |& getline tmp – 既然命令已经执行完成,就要用 getline 获取其输出。前
面命令的输出会被存在变量 ”tmp” 中。
print tmp – 打印输出
close(command) – 最后,关闭命令。
双向管道迟早会派上用场,尤其是当 awk 对外部程序的输出依赖比较大时。
99. 系统函数
可以使用操作系统内置的函数来执行操作系统命令,但请注意,调用系统命令和使用双向管
道是不同的。
使用 ”|&” 时,可以把任意 awk 命令的输出作为外部命令的输入;也可以接收外部命令的输
出作为 awk 的输入 ( 要不怎么叫双向管道呢 ) 。
执行系统命令时,可以传递任意的字符串作为命令的参数,它会被当做操作系统命令准确第
执行,并返回结果 ( 这和双向管道有所不同 ) 。
下面的例子在 awk 中调用 pwd 和 date 命令:
$ awk 'BEGIN { system("pwd") }'
/root
$ awk 'BEGIN { system("date") }'
Wed Apr 10 17:07:08 CST 2013
在执行比较大的 awk 脚本时,你或许想在脚本开始和结束时发送一封电子邮件。下面的例
子展示如何在 BEGIN 和 END 区域中调用系统命令,在脚本开始和结束时发送邮件。
$ cat system.awk
BEGIN {
system("echo 'Started' | mail -s 'Program system.awk started ..' ramesh@thegeekstuff.com");
}
{
split($2,quantity,",");
total=0;
for (x in quantity)
total=total+quantity[x];
print "Item",$1,":",total,"quantities sold";
}
END {
system("echo
'Completed'
|
mail
-s 'Program system.awk completed..'
ramesh@thegeekstuff.com");
}
$ awk -f system.awk items-sold.txt
Item 101 : 2 quantities sold
Item 102 : 0 quantities sold
Item 103 : 10 quantities sold
Item 104 : 2 quantities sold
Item 105 : 10 quantities sold
100. 时间函数
这些命令仅适用于 GAWK 。
看下面的例子, systime() 函数返回系统的 POSIX 时间,即自 1970 年 1 月 1 日起至今经过的
秒数。
$ awk 'BEGIN { print systime() }'
1365585325
如果使用 strftime 函数把 POSIX 时间转换为可读的格式, systime 函数就变动很有用了。
下面例子使用 systime 和 strftime 以可读的格式打印当前时间。
$ awk 'BEGIN { print strftime("%c",systime()) }'
Wed Apr 10 17:17:35 2013
下面的例子展示了多种可用的时间格式。
$ cat strftime.awk
BEGIN {
print "--- basic formats ---"
print strftime("Format 1: %m/%d/%Y %H:%M:%S",systime())
print strftime("Format 2: %m/%d/%y %I:%M:%S %p",systime())
print strftime("Format 3: %m-%b-%Y %H:%M:%S",systime())
print strftime("Format 4: %m-%b-%Y %H:%M:%S %Z",systime())
print strftime("Format 5: %a %b %d %H:%M:%S %Z %Y",systime())
print strftime("Format 6: %A %B %d %H:%M:%S %Z %Y",systime())
print "--- quick formats ---"
print strftime("Format 7: %c",systime())
print strftime("Foramt 8: %D",systime())
print strftime("Format 9: %F",systime())
print strftime("Format 10: %x",systime())
print strftime("Format 11: %X",systime())
print "--- single line format with %t ---"
print strftime("%Y %t%B %t%d",systime())
print "--- multi line format with %n ---"
print strftime("%Y%n%B%n%d",systime())
}
$ awk -f strftime.awk
--- basic formats ---
Format 1: 04/10/2013 18:04:06
Format 2: 04/10/13 06:04:06 PM
Format 3: 04-Apr-2013 18:04:06
Format 4: 04-Apr-2013 18:04:06 CST Format 5: Wed Apr 10 18:04:06 CST 2013
Format 6: Wednesday April 10 18:04:06 CST 2013
--- quick formats ---
Format 7: Wed Apr 10 18:04:06 2013
Foramt 8: 04/10/13
Format 9: 2013-04-10
Format 10: 04/10/13
Format 11: 18:04:06
--- single line format with %t ---
2013 April 10
--- multi line format with %n ---
2013
April
10
下面是 strftime 函数可用的时间格式标识符,需要注意的是,下面所有的标识符依赖于本地
系统的设置,下面的示例是打印英文时间。
101. getline 命令
如你所知,没从输入文件读取一行, body 区域的代码就会执行一次。你无法干预, awk 自
动执行这个过程。
然而使用 geline 命令可以控制 awk 从输入文件 ( 或其他文件 ) 读取数据。注意,一旦 getline
执行完成, awk 脚本会重置 NF,NR,FNR 和 $0 等内置变量。
getline 示例
$ awk -F"," '{getline;print $0;}' items.txt
102,Refrigerator,Appliance,850,2
104,Tennis Racket,Sports,190,20
105,Laser Printer,Office,475,5
当在 body 区域使用了 getline 时,会直接读取下一行数据。这个例子中,第一条语句便是
getline ,所以即使 awk 已经读取了第一行数据, getline 也会继续读取下一行,因为我们强制
它读取下一行,因此, getline 后面的 print $0 会打印输入文件的第二行。
这个例子中:
开始执行 body 区域时,执行任何命令之前, awk 从 items.txt 文件中读取第一行数
据,保存在变量 $0 中
getline – 我们用 getline 命令强制 awk 读取下一行数据,保存在变量 $0 中 ( 之前的内
容被覆盖掉了 )
print $0 – 既然现在 $0 中保存的是第二行数据, print $0 会打印文件第二行 ( 而不是第
一行 )
z body 区域继续执行,只打印偶数行的数据。 ( 注意到最后一行 105 也打印了么? )
把 getline 的内容保存的变量中
除了把 getline 的内容放到 $0 中,还可以把它保存在变量中。
只打印奇数行内容
$ awk -F"," '{getline tmp; print $0;}' items.txt
101,HD Camcorder,Video,210,10
103,MP3 Player,Audio,270,15
105,Laser Printer,Office,475,5
这个例子如何工作:
开始执行 body 区域时,执行任何命令之前, awk 从 items.txt 文件中读取第一行数
据,保存在变量 $0 中
getline tmp – 强制 awk 读取下一行,并保存在变量 tmp 中
print $0 – 此时 $0 仍然是第一行数据,因为 getline tmp 没有覆盖 $0, 因此会打印第 一行数据 ( 而不是第二行 )
body 区域继续执行,只打印奇数行的数据。
下面的例子同时打印 $0 和 tmp ,可以看到, $0 是奇数行,而 tmp 是偶数行
$ awk -F"," '{getline tmp; print "$0->",$0;print "tmp->",tmp;}' items.txt
$0-> 101,HD Camcorder,Video,210,10
tmp-> 102,Refrigerator,Appliance,850,2
$0-> 103,MP3 Player,Audio,270,15
tmp-> 104,Tennis Racket,Sports,190,20
$0-> 105,Laser Printer,Office,475,5
tmp-> 104,Tennis Racket,Sports,190,20
从其他的文件 getline 内容
上面两个例子, getline 都是从当前输入文件获取内容,其实也可以从其他文件 ( 非当前输入
文件 ) 读取内容,如下所示。
在两个文件中循环切换,打印所有内容。
$ awk -F"," '{print $0;getline <"items-sold.txt"; print $0;}' items.txt
101,HD Camcorder,Video,210,10
101 2 10 5 8 10 12
102,Refrigerator,Appliance,850,2
102 0 1 4 3 0 2
103,MP3 Player,Audio,270,15
103 10 6 11 20 5 13
104,Tennis Racket,Sports,190,20
104 2 3 4 0 6 5
105,Laser Printer,Office,475,5
105 10 2 5 7 12 6
这个例子中:
开始执行 body 区域时,执行任何命令之前, awk 从 items.txt 文件中读取第一行数
据,保存在变量 $0 中
z print $0 – 打印 items.txt 文件的第一行
z getline <”items-sold.txt” – 读取 items-sold.txt 中第一行并保存在 $0 中
z print $0 – 打印 items-sold.txt 文件中的第一行
z body 区域继续执行,轮番打印 items.txt 和 items-sold.txt 的剩余内容
从其他的文件 getline 内容到变量中
除了把两个文件的内容都读到 $0 中之外,也可以使用 ”getline var” 把读取的内容保存到变量
中。
继续使用上面那两个文件,打印所有内容 ( 使用 tmp 变量 )
$ awk -F"," '{print $0; getline tmp < "items-sold.txt";print tmp;}' items.txt
101,HD Camcorder,Video,210,10
101 2 10 5 8 10 12 102,Refrigerator,Appliance,850,2
102 0 1 4 3 0 2
103,MP3 Player,Audio,270,15
103 10 6 11 20 5 13
104,Tennis Racket,Sports,190,20
104 2 3 4 0 6 5
105,Laser Printer,Office,475,5
105 10 2 5 7 12 6
除了把第二个文件的内容保存到变量之外,这个例子和之前的例子是相同的。
getline 执行外部命令
getline 也可以执行 UNIX 命令并获取其输出。
下面的例子使用 getline 获取 date 命令的输出并打印出来。请注意这里也要使用 close 刚执
行的命令, date 命令的输出保存在变量 $0 中,如下所示。
使用这个方法可以在输出报文的 header 和 footer 中显示时间戳。
$ cat getline1.awk
BEGIN {
FS=",";
"date" | getline
close("date")
print "Timestamp:" $0
}
{
if ( $5 <= 5)
print "Buy More:Order",$2,"immediately!"
else
print "Sell More:Give discount on",$2,"immediatelty!"
}
$ awk -f getline1.awk items.txt
Timestamp:Thu Apr 11 11:07:47 CST 2013
Sell More:Give discount on HD Camcorder immediatelty!
Buy More:Order Refrigerator immediately!
Sell More:Give discount on MP3 Player immediatelty!
Sell More:Give discount on Tennis Racket immediatelty!
Buy More:Order Laser Printer immediately!
除了把命令输出保存在 $0 中之外,也可以把它保存在任意的 awk 变量中 ( 如 timestamp), 如下
所示。
$ cat getline2.awk
BEGIN { FS=",";
"date" | getline timestamp
close("date")
print "Timestamp:" timestamp
}
{
if ( $5 <= 5)
print "Buy More: Order",$2,"immediately!"
else
print "Sell More: Give discount on",$2,"immediately!"
}
$ awk -f getline2.awk items.txt
Timestamp:Thu Apr 11 11:20:05 CST 2013
Sell More: Give discount on HD Camcorder immediately!
Buy More: Order Refrigerator immediately!
Sell More: Give discount on MP3 Player immediately!
Sell More: Give discount on Tennis Racket immediately!
Buy More: Order Laser Printer immediately!
资料来源于《SedandAwk101Hacks》,大家有兴趣可以买一本,也可以关注我,我更新完它。
曾经,我花费大半月将它们跑完,现在啥都忘了,还是要常用。
只为学习交流,不为获利,侵权联系立删。