gawk 是 Unix 中的原始 awk 程序的 GUN 版本。

$0 代表整个文本行;

$1 代表第一个数据字段;

….

  1. # gawk 会从 STDIN 接收数据,输入任何值,都会打印 hello world
  2. $ gawk '{print "hello world"}'
  3. # 从文件中获取输入,打印第一列
  4. $ gawk '{print $1}' data.txt
  5. # 多个命令
  6. $ echo "My name is Rich" | gawk '{$4="xiaocan"; print $0}'
  7. My name is xiaocan

BEGIN

  1. # 在处理数据前运行脚本使用 BEGIN
  2. $ gawk 'BEGIN {print "hello world"}'
  3. hello world
  4. $ gawk 'BEGIN {print "hello world"}
  5. > {print $0}' data4.txt
  6. hello world
  7. This is a test of the test script;
  8. This is the second test of the test script;

END

  1. # 在处理数据后运行脚本使用 END
  2. $ gawk 'BEGIN {print "hello world"}
  3. > {print $0}
  4. > END {print "End of file"}' data4.txt
  5. hello world
  6. This is a test of the test script;
  7. This is the second test of the test script;
  8. End of file

分隔符

每个字段通过分隔符划分,默认是任意的空白字符。可以指定其它的。

  1. # 指定分隔符
  2. $ gawk -F: '{print $1}' /etc/passwd
  3. # 可以使用 FS 变量定义字段分隔符
  4. $ gawk 'BEGIN {print "This PC user list:"; FS=":"}
  5. > {print $1}
  6. > END {print "all done"}' /etc/passwd

指定文件 -f

  1. $ cat script2.gawk
  2. {print $1 "'s home directory is " $6}
  3. # 指定命令文件
  4. $ gawk -F: -f script2.gawk /etc/passwd

自定义变量

变量区分大小写。

  1. # 注意,不用使用 $ 引用
  2. $ gawk '
  3. > BEGIN{
  4. > testing="This is a test"
  5. > print testing
  6. > }'
  7. This is a test
  8. $ gawk 'BEGIN{x=4;x=x*2+3;print x}'
  9. 11
  10. # 文件中使用变量和多行
  11. $ cat script3.gawk
  12. {
  13. text = "'s home directory is "
  14. print $1 text $6
  15. }
  16. $ gawk -F: -f script3.gawk /etc/passwd
  17. root's home directory is /root
  18. daemon's home directory is /usr/sbin
  19. bin's home directory is /bin
  20. sys's home directory is /dev
  21. ...

在命令行上给变量赋值

  1. $ cat script1
  2. BEGIN {FS=","}
  3. {print $n}
  4. $ gawk -f script1 n=3 data1
  5. data13
  6. data23
  7. data33
  8. $ cat script2
  9. BEGIN {print "The starting value is",n; FS=","}
  10. {print $n}
  11. # 变量在 BEGIN 中不能使用
  12. $ gawk -f script2 n=3 data1
  13. The starting value is
  14. data13
  15. data23
  16. data33
  17. # 使用 -v 参数,就可以在 BEGIN 中使用变量
  18. $ gawk -v n=3 -f script2 data1
  19. The starting value is 3
  20. data13
  21. data23
  22. data33

内建变量

字段分隔符和记录分隔符变量

变量 描述
FIELDWIDTHS 由空格分隔的一列数字,定义了每个数据字段确切宽度
FS 输入字段分隔符,默认为空格
RS 输入记录分隔符,默认为换行符
OFS 输出字段分隔符,默认为空格
ORS 输出记录分隔符,默认为换行符
  1. $ cat data1
  2. data11,data12,data13,data14,data15
  3. data21,data22,data23,data24,data25
  4. data31,data32,data33,data34,data35
  5. $ gawk 'BEGIN{FS=","} {print $1,$2,$3}' data1
  6. data11 data12 data13
  7. data21 data22 data23
  8. data31 data32 data33
  9. # 指定输出字段分隔符
  10. $ gawk 'BEGIN{FS=","; OFS="--"} {print $1,$2,$3}' data1
  11. data11--data12--data13
  12. data21--data22--data23
  13. data31--data32--data33
  14. $ cat data1b
  15. 1005.23424234.37
  16. 115-2.3453450.00
  17. 0582.23442133.23
  18. # 使用 FIELDWIDTHS 分隔
  19. $ gawk 'BEGIN{FIELDWIDTHS="3 5 2 6"} {print $1,$2,$3,$4}' data1b
  20. 100 5.234 24 234.37
  21. 115 -2.34 53 450.00
  22. 058 2.234 42 133.23

可以把 RS 变量设制成空字符串,然后在数据记录间留一个空白行。gawk 会把每个空白行当作一个记录分隔符。

  1. $ cat data2
  2. xiaocan
  3. 123456789
  4. zhangsan
  5. 123456789
  6. xxxxx
  7. 2342424234
  8. $ gawk 'BEGIN{RS="";FS="\n"} {print $1,$2}' data2
  9. xiaocan 123456789
  10. zhangsan 123456789
  11. xxxxx 2342424234

数据变量

ARGC 当前命令行参数个数

ARGV 包含命令行参数的数组

  1. # 注意,程序脚本不会当作命令行参数的一部分
  2. $ gawk 'BEGIN{print ARGC,ARGV[1]}' data1
  3. 2 data1

ENVIRON 当前 shell 环境变量及其值组成的关联数组,可以用它获取任意的环境变量

  1. $ gawk '
  2. > BEGIN{
  3. > print ENVIRON["HOME"]
  4. > print ENVIRON["PATH"]
  5. > }'
  6. /home/xiaocan
  7. /home/xiaocan/gems/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

NF 字段总数,可以用它获取最后一个字段

  1. $ gawk 'BEGIN{FS=":"; OFS=":"} {print $1,$NF}' /etc/passwd
  2. root:/bin/bash
  3. daemon:/usr/sbin/nologin
  4. bin:/usr/sbin/nologin
  5. sys:/usr/sbin/nologin
  6. sync:/bin/sync
  7. games:/usr/sbin/nologin
  8. man:/usr/sbin/nologin
  9. ...

NR 当前处理的所有文件已处理的行数,同时处理多个文件会累加。

FNR 当前处理的文件已处理的行数

  1. $ gawk '
  2. > BEGIN {FS=","}
  3. > {print $1, "FNR="FNR, "NR="NR}
  4. > END {print "There were",NR,"records processed"}' data1 data1
  5. data11 FNR=1 NR=1
  6. data21 FNR=2 NR=2
  7. data31 FNR=3 NR=3
  8. data11 FNR=1 NR=4
  9. data21 FNR=2 NR=5
  10. data31 FNR=3 NR=6
  11. There were 6 records processed

处理数组

gawk 里面的数组更像是散列表:

  1. $ gawk 'BEGIN{
  2. > capital["China"] = "Beijing"
  3. > print capital["China"]
  4. > }'
  5. Beijing
  6. $ gawk 'BEGIN{
  7. > var[1] = 2
  8. > var[2] = 3
  9. > print var[1] + var[2]
  10. > }'
  11. 5

使用 for 遍历数组:

  1. $ gawk 'BEGIN{
  2. > var["a"] = 1
  3. > var["b"] = 2
  4. > var["c"] = 3
  5. > for (test in var)
  6. > {
  7. > print "Index:",test," - value:",var[test]
  8. > }
  9. > }'
  10. Index: a - value: 1
  11. Index: b - value: 2
  12. Index: c - value: 3

数据数组变量:

  1. $ gawk 'BEGIN{
  2. > var["a"] = 1
  3. > var["b"] = 2
  4. > var["c"] = 3
  5. > delete var["c"]
  6. > print var["c"]
  7. > }'

使用模式

正则表达式

正则表达式必须出现在它要控制的程序脚本的左花括号前。

  1. # 过滤并打印
  2. $ echo "This is a test" | gawk "/test/{print $0}"

匹配操作符

它可以将正则表达式限定在记录中的特定数据字段。比如 $1 ~ /^data/ ,匹配第一个字段以 data 开头的记录。

  1. $ cat data1
  2. data11,data12,data13,data14,data15
  3. data21,data22,data23,data24,data25
  4. data31,data32,data33,data34,data35
  5. $ gawk 'BEGIN{FS=","} $2 ~ /^data2/{print $0}' data1
  6. data21,data22,data23,data24,data25

可以用使用 ! 来排除正则表达式的匹配:

  1. $ gawk 'BEGIN{FS=":"} $1 !~ /root/{print $1,$NF}' /etc/passwd
  2. daemon /usr/sbin/nologin
  3. bin /usr/sbin/nologin
  4. sys /usr/sbin/nologin

数学表达式

  1. $ gawk -F: '$4==0 {print $1}' /etc/passwd
  2. root

支持的数据表达式有:

x == y

x <= y

x < y

x >= y

x > y

也可以的文本数据使用表达式:

  1. $ gawk -F: '$1 == "root" {print $0}' /etc/passwd
  2. root:x:0:0:root:/root:/bin/bash

结构化命令

if 语句

  1. $ cat data4
  2. 10
  3. 5
  4. 13
  5. 50
  6. 34
  7. 6
  8. $ gawk '{if($1 > 40) print $1}' data4
  9. 50
  10. # if-else
  11. $ gawk '{
  12. > if($1>20)
  13. > {
  14. > x = $1 * 2;
  15. > }else{
  16. > x = $1 / 2
  17. > }
  18. > print x
  19. > }' data4
  20. 5
  21. 2.5
  22. 6.5
  23. 100
  24. 68
  25. 3
  26. # 单行命令
  27. $ gawk '{if($1>20)print $1*2; else print $1/2}' data4
  28. 5
  29. 2.5
  30. 6.5
  31. 100
  32. 68
  33. 3

while 语句

  1. $ cat data5
  2. 130 120 135
  3. 110 110 130
  4. 160 170 180
  5. 220 220 225
  6. $ gawk '{
  7. total = 0
  8. i = 1
  9. while (i < 4)
  10. {
  11. total += $i
  12. i++
  13. }
  14. print total, total/3
  15. }' data5
  16. 385 128.333
  17. 350 116.667
  18. 510 170
  19. 665 221.667

还可以使用 break 和 continue 语句。

do while 语句

  1. $ gawk '{
  2. > total=0
  3. > i = 1
  4. > do{
  5. > total+=$i
  6. > i++
  7. > } while(i<4)
  8. > print total, total/3
  9. > }' data5
  10. 385 128.333
  11. 350 116.667
  12. 510 170
  13. 665 221.667

for 语句

  1. $ gawk '{
  2. > total = 0
  3. > for (i=1;i<4;i++)
  4. > {
  5. > total+=$i
  6. > }
  7. > avg = total/3
  8. > print "Average:", avg
  9. > }' data5
  10. Average: 128.333
  11. Average: 116.667
  12. Average: 170
  13. Average: 221.667

格式化打印

使用 printf 格式化打印。格式化的格式是:%[modifier]control-letter

Control-letter 控制字母有:

e 用科学记数法显示一个数

d 显示一个整数值

f 显示一个浮点数

s 显示文本字符串

modifier 修饰符有:

width:指定长度,如果小于这个值,会将文本右对齐,用空格进行填充;

prec:数字值,指定浮点数中小数点后面位数

减号:使用左对齐

  1. $ gawk 'BEGIN{
  2. > x = 10 * 100
  3. > printf "The answer is: %e\n",x
  4. > }'
  5. The answer is: 1.000000e+03
  6. $ gawk 'BEGIN{FS=","} {printf "%s ", $1} END{printf "\n"}' data1
  7. data11 data21 data31
  8. $ gawk '{
  9. > total=0
  10. > for (i=0;i<4;i++)
  11. > {
  12. > total+=$i
  13. > }
  14. > avg=total/3
  15. > printf "Average: %5.1f\n",avg
  16. > }' data5
  17. Average: 128.3
  18. Average: 116.7
  19. Average: 170.0
  20. 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) 字符串转大写

  1. $ gawk 'BEGIN{x="testing"; print toupper(x); print length(x)}'
  2. TESTING
  3. 7

时间函数

systime() 返回当前时间

自定义函数

函数需要在所有代码块之前定义:

  1. $ gawk '
  2. > function myprint()
  3. > {
  4. > printf "%-16s - %s\n", $1, $2
  5. > }
  6. > BEGIN{FS="\n"; RS=""}
  7. > {
  8. > myprint()
  9. > }' data2
  10. xiaocan - 123456789
  11. zhangsan - 123456789
  12. xxxxx - 2342424234

将函数保存到其它文件中:

  1. $ cat funclib
  2. function myprint()
  3. {
  4. printf "%-16s - %s\n", $1, $2
  5. }
  6. $ cat script4
  7. BEGIN{ FS="\n"; RS=""}
  8. {
  9. myprint()
  10. }
  11. $ gawk -f funclib -f script4 data2
  12. xiaocan - 123456789
  13. zhangsan - 123456789
  14. xxxxx - 2342424234

示例

下面的文件中,第一列是项目名称,第二列是开发者,后面是每天的代码提交量。我们来统计一下每个开发者在所有项目中的代码量。

  1. $ cat git-code.txt
  2. project-one,zhangsan,80,12,56,23,12
  3. project-one,lisi,12,23,45,23,120
  4. project-two,wangwu,234,23,35,77,34
  5. project-one,zhangsan,23,34,32,67,34

下面是统计的脚本:

  1. $ cat code.sh
  2. #!/bin/bash
  3. for author in $(gawk -F, '{print $2}' git-code.txt | sort | uniq)
  4. do
  5. gawk -v author=$author 'BEGIN{FS=","; total=0}
  6. {
  7. if($2 == author)
  8. {
  9. total+= $3 + $4 + $5 + $6 + $7
  10. }
  11. }
  12. END {
  13. print "Total code for", author, "is", total
  14. }' git-code.txt
  15. done
  16. # 运行结果
  17. $ sh code.sh
  18. Total code for lisi is 223
  19. Total code for wangwu is 403
  20. Total code for zhangsan is 373