gawk 是 Unix 中的原始 awk 程序的 GUN 版本。
$0
代表整个文本行;
$1
代表第一个数据字段;
….
# gawk 会从 STDIN 接收数据,输入任何值,都会打印 hello world
$ gawk '{print "hello world"}'
# 从文件中获取输入,打印第一列
$ gawk '{print $1}' data.txt
# 多个命令
$ echo "My name is Rich" | gawk '{$4="xiaocan"; print $0}'
My name is xiaocan
BEGIN
# 在处理数据前运行脚本使用 BEGIN
$ gawk 'BEGIN {print "hello world"}'
hello world
$ gawk 'BEGIN {print "hello world"}
> {print $0}' data4.txt
hello world
This is a test of the test script;
This is the second test of the test script;
END
# 在处理数据后运行脚本使用 END
$ gawk 'BEGIN {print "hello world"}
> {print $0}
> END {print "End of file"}' data4.txt
hello world
This is a test of the test script;
This is the second test of the test script;
End of file
分隔符
每个字段通过分隔符划分,默认是任意的空白字符。可以指定其它的。
# 指定分隔符
$ gawk -F: '{print $1}' /etc/passwd
# 可以使用 FS 变量定义字段分隔符
$ gawk 'BEGIN {print "This PC user list:"; FS=":"}
> {print $1}
> END {print "all done"}' /etc/passwd
指定文件 -f
$ cat script2.gawk
{print $1 "'s home directory is " $6}
# 指定命令文件
$ gawk -F: -f script2.gawk /etc/passwd
自定义变量
变量区分大小写。
# 注意,不用使用 $ 引用
$ gawk '
> BEGIN{
> testing="This is a test"
> print testing
> }'
This is a test
$ gawk 'BEGIN{x=4;x=x*2+3;print x}'
11
# 文件中使用变量和多行
$ cat script3.gawk
{
text = "'s home directory is "
print $1 text $6
}
$ gawk -F: -f script3.gawk /etc/passwd
root's home directory is /root
daemon's home directory is /usr/sbin
bin's home directory is /bin
sys's home directory is /dev
...
在命令行上给变量赋值
$ cat script1
BEGIN {FS=","}
{print $n}
$ gawk -f script1 n=3 data1
data13
data23
data33
$ cat script2
BEGIN {print "The starting value is",n; FS=","}
{print $n}
# 变量在 BEGIN 中不能使用
$ gawk -f script2 n=3 data1
The starting value is
data13
data23
data33
# 使用 -v 参数,就可以在 BEGIN 中使用变量
$ gawk -v n=3 -f script2 data1
The starting value is 3
data13
data23
data33
内建变量
字段分隔符和记录分隔符变量
变量 | 描述 |
---|---|
FIELDWIDTHS | 由空格分隔的一列数字,定义了每个数据字段确切宽度 |
FS | 输入字段分隔符,默认为空格 |
RS | 输入记录分隔符,默认为换行符 |
OFS | 输出字段分隔符,默认为空格 |
ORS | 输出记录分隔符,默认为换行符 |
$ cat data1
data11,data12,data13,data14,data15
data21,data22,data23,data24,data25
data31,data32,data33,data34,data35
$ gawk 'BEGIN{FS=","} {print $1,$2,$3}' data1
data11 data12 data13
data21 data22 data23
data31 data32 data33
# 指定输出字段分隔符
$ gawk 'BEGIN{FS=","; OFS="--"} {print $1,$2,$3}' data1
data11--data12--data13
data21--data22--data23
data31--data32--data33
$ cat data1b
1005.23424234.37
115-2.3453450.00
0582.23442133.23
# 使用 FIELDWIDTHS 分隔
$ gawk 'BEGIN{FIELDWIDTHS="3 5 2 6"} {print $1,$2,$3,$4}' data1b
100 5.234 24 234.37
115 -2.34 53 450.00
058 2.234 42 133.23
可以把 RS
变量设制成空字符串,然后在数据记录间留一个空白行。gawk 会把每个空白行当作一个记录分隔符。
$ cat data2
xiaocan
123456789
zhangsan
123456789
xxxxx
2342424234
$ gawk 'BEGIN{RS="";FS="\n"} {print $1,$2}' data2
xiaocan 123456789
zhangsan 123456789
xxxxx 2342424234
数据变量
ARGC
当前命令行参数个数
ARGV
包含命令行参数的数组
# 注意,程序脚本不会当作命令行参数的一部分
$ gawk 'BEGIN{print ARGC,ARGV[1]}' data1
2 data1
ENVIRON
当前 shell 环境变量及其值组成的关联数组,可以用它获取任意的环境变量
$ gawk '
> BEGIN{
> print ENVIRON["HOME"]
> print ENVIRON["PATH"]
> }'
/home/xiaocan
/home/xiaocan/gems/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
NF
字段总数,可以用它获取最后一个字段
$ gawk 'BEGIN{FS=":"; OFS=":"} {print $1,$NF}' /etc/passwd
root:/bin/bash
daemon:/usr/sbin/nologin
bin:/usr/sbin/nologin
sys:/usr/sbin/nologin
sync:/bin/sync
games:/usr/sbin/nologin
man:/usr/sbin/nologin
...
NR
当前处理的所有文件已处理的行数,同时处理多个文件会累加。
FNR
当前处理的文件已处理的行数
$ 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
处理数组
gawk 里面的数组更像是散列表:
$ gawk 'BEGIN{
> capital["China"] = "Beijing"
> print capital["China"]
> }'
Beijing
$ gawk 'BEGIN{
> var[1] = 2
> var[2] = 3
> print var[1] + var[2]
> }'
5
使用 for 遍历数组:
$ gawk 'BEGIN{
> var["a"] = 1
> var["b"] = 2
> var["c"] = 3
> for (test in var)
> {
> print "Index:",test," - value:",var[test]
> }
> }'
Index: a - value: 1
Index: b - value: 2
Index: c - value: 3
数据数组变量:
$ gawk 'BEGIN{
> var["a"] = 1
> var["b"] = 2
> var["c"] = 3
> delete var["c"]
> print var["c"]
> }'
使用模式
正则表达式
正则表达式必须出现在它要控制的程序脚本的左花括号前。
# 过滤并打印
$ echo "This is a test" | gawk "/test/{print $0}"
匹配操作符
它可以将正则表达式限定在记录中的特定数据字段。比如 $1 ~ /^data/
,匹配第一个字段以 data 开头的记录。
$ cat data1
data11,data12,data13,data14,data15
data21,data22,data23,data24,data25
data31,data32,data33,data34,data35
$ gawk 'BEGIN{FS=","} $2 ~ /^data2/{print $0}' data1
data21,data22,data23,data24,data25
可以用使用 !
来排除正则表达式的匹配:
$ gawk 'BEGIN{FS=":"} $1 !~ /root/{print $1,$NF}' /etc/passwd
daemon /usr/sbin/nologin
bin /usr/sbin/nologin
sys /usr/sbin/nologin
数学表达式
$ gawk -F: '$4==0 {print $1}' /etc/passwd
root
支持的数据表达式有:
x == y
x <= y
x < y
x >= y
x > y
也可以的文本数据使用表达式:
$ gawk -F: '$1 == "root" {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
结构化命令
if 语句
$ cat data4
10
5
13
50
34
6
$ gawk '{if($1 > 40) print $1}' data4
50
# if-else
$ gawk '{
> if($1>20)
> {
> x = $1 * 2;
> }else{
> x = $1 / 2
> }
> print x
> }' data4
5
2.5
6.5
100
68
3
# 单行命令
$ gawk '{if($1>20)print $1*2; else print $1/2}' data4
5
2.5
6.5
100
68
3
while 语句
$ cat data5
130 120 135
110 110 130
160 170 180
220 220 225
$ gawk '{
total = 0
i = 1
while (i < 4)
{
total += $i
i++
}
print total, total/3
}' data5
385 128.333
350 116.667
510 170
665 221.667
还可以使用 break 和 continue 语句。
do while 语句
$ gawk '{
> total=0
> i = 1
> do{
> total+=$i
> i++
> } while(i<4)
> print total, total/3
> }' data5
385 128.333
350 116.667
510 170
665 221.667
for 语句
$ gawk '{
> total = 0
> for (i=1;i<4;i++)
> {
> total+=$i
> }
> avg = total/3
> print "Average:", avg
> }' data5
Average: 128.333
Average: 116.667
Average: 170
Average: 221.667
格式化打印
使用 printf
格式化打印。格式化的格式是:%[modifier]control-letter
Control-letter 控制字母有:
e
用科学记数法显示一个数
d
显示一个整数值
f
显示一个浮点数
s
显示文本字符串
…
modifier 修饰符有:
width:指定长度,如果小于这个值,会将文本右对齐,用空格进行填充;
prec:数字值,指定浮点数中小数点后面位数
减号:使用左对齐
$ gawk 'BEGIN{
> x = 10 * 100
> printf "The answer is: %e\n",x
> }'
The answer is: 1.000000e+03
$ gawk 'BEGIN{FS=","} {printf "%s ", $1} END{printf "\n"}' data1
data11 data21 data31
$ gawk '{
> total=0
> for (i=0;i<4;i++)
> {
> total+=$i
> }
> avg=total/3
> printf "Average: %5.1f\n",avg
> }' data5
Average: 128.3
Average: 116.7
Average: 170.0
Average: 221.7
内建函数
数学函数
exp(x)
x的指数函数
int(x)
x的整数部分
rand()
生成0到1 之间的随机浮点数
sqrt()
x的平方根
比如 x=int(10 * rand())
生成 0 到 10 的随机数。
gawk 还支持一些按位操作数据的函数。
字符串函数
length([s])
返回字符串 s 的长度,如果没有指定的话,返回 $0 的长度。
tolower(s)
字符串转小写
toupper(s)
字符串转大写
$ gawk 'BEGIN{x="testing"; print toupper(x); print length(x)}'
TESTING
7
时间函数
systime()
返回当前时间
自定义函数
函数需要在所有代码块之前定义:
$ gawk '
> function myprint()
> {
> printf "%-16s - %s\n", $1, $2
> }
> BEGIN{FS="\n"; RS=""}
> {
> myprint()
> }' data2
xiaocan - 123456789
zhangsan - 123456789
xxxxx - 2342424234
将函数保存到其它文件中:
$ cat funclib
function myprint()
{
printf "%-16s - %s\n", $1, $2
}
$ cat script4
BEGIN{ FS="\n"; RS=""}
{
myprint()
}
$ gawk -f funclib -f script4 data2
xiaocan - 123456789
zhangsan - 123456789
xxxxx - 2342424234
示例
下面的文件中,第一列是项目名称,第二列是开发者,后面是每天的代码提交量。我们来统计一下每个开发者在所有项目中的代码量。
$ cat git-code.txt
project-one,zhangsan,80,12,56,23,12
project-one,lisi,12,23,45,23,120
project-two,wangwu,234,23,35,77,34
project-one,zhangsan,23,34,32,67,34
下面是统计的脚本:
$ cat code.sh
#!/bin/bash
for author in $(gawk -F, '{print $2}' git-code.txt | sort | uniq)
do
gawk -v author=$author 'BEGIN{FS=","; total=0}
{
if($2 == author)
{
total+= $3 + $4 + $5 + $6 + $7
}
}
END {
print "Total code for", author, "is", total
}' git-code.txt
done
# 运行结果
$ sh code.sh
Total code for lisi is 223
Total code for wangwu is 403
Total code for zhangsan is 373