GNU/Linux awk命令用法详解

本来计划年前写完的,结果现在才搞定,手册的内容一边翻一边实验,还有awk版本问题,又参考Effective AWK Programming对语法和示例做了些补充。终于写完了,大家元宵节快乐!

AWK简介

AWK是一门解释型的编程语言,它的名字来源于它的三位作者的姓氏:Alfred Aho,Peter Weinberger和Brian Kernighan。AWK能够应用于广泛的计算和数据处理任务。所有的GNU/Linux发行版都自带GAWK,即GNU AWK,是AWK的扩展并且与AWK完全兼容。

上一篇文章讲到的sed命令类似,AWK逐行读取输入流中的内容,并对读取的行执行所有命令,如此循环,直到输入流结束。

本文基于GAWK进行介绍,因为GAWK比原生AWK使用更普遍些。

程序结构

AWK命令格式如下:

awk [options] 'program' input-file1 input-file2 ...

或者

awk [options] -f program-file input-file1 input-file2 ...

第一种格式中,awk从file中获取输入流,然后执行单引号内的程序。第二种格式则是从文件program-file中获取将要执行的程序。

上述AWK命令的program部分的结构可分为三块:BEGIN、BODY和END。

BEGIN:在AWK命令的一开始执行的动作,它只执行一次,可以把变量初始化放在这里。注意,BEGIN部分是可选的,并且一个AWK命令中可以有多个BEGIN块。另外,如果有-v选项的赋值操作,则-v的操作在BEGIN之前。

BEGIN块的写法为:BEGIN{…}

BODY:程序主体,对输入流的每一行执行动作。如果存在BEGIN或END,则这部分是可选的。一个AWK命令中可以有多个BODY块。

BODY块的写法为:{…}

END:在AWK命令的最后执行的动作,它只执行一次。注意,END部分是可选的,并且一个AWK命令中可以有多个END块。另外,使用END { }块时,awk的file参数不能省略。

END块的写法为:END{…}

例如下面的命令:
[root@ubuntu]awk_test:$ awk 'BEGIN{print "Output start!"}; {print}; END{print "Output done!"}' awk_test.txt 
Output start!
USER       PID %MEM    VSZ   RSS STAT START   TIME COMMAND
root         1  0.1   3652  1916 Ss   Jan07   0:03 /sbin/init
root         2  0.0      0     0 S    Jan07   0:00 [kthreadd]
root         3  0.0      0     0 S    Jan07   0:02 [ksoftirqd/0]
root        26  0.0      0     0 S    Jan07   0:40 [kswapd0]
user       495  0.1   3588  1092 Ss   Jan07   0:00 /sbin/udevd --daemon
user       860  0.0   3584   908 S    Jan07   0:01 /sbin/udevd --daemon
user      1137  0.0   4520   776 S    Jan07   0:00 smbd -F
user      1550  0.1   4521  1816 Ss   Jan07   0:15 nmbd -D
Output done!

可以看到这条命令在命令的开始打印一行输出“Output start!”,然后对文件中的每一行内容执行print操作,最后又打印出一行输出“Output done!”。

从句法结构上来讲,program由一条条规则组成,每条规则由模式动作组成,即模式匹配后执行相应的动作。动作放在花括号内以与模式区分。所以,program一般的格式是这样的:

pattern { action }

pattern { action }

那么,下面这条命令(打印长度大于80字符的行):

awk 'length($0) > 80' data

可以看到这条awk命令只有pattern而没有action部分。如果没有action部分,则执行默认动作:打印整个record。

也可以把program写到文件里(不用加单引号),通过AWK的第二种格式来执行。
[root@ubuntu]awk_test:$ cat progfile 
BEGIN{print "Output start!"};
{print};
END{print "Output done!"};
[root@ubuntu]awk_test:$ awk –f progfile awk_test.txt

为了方便后期维护,建议将AWK的程序文件以.awk作为后缀名。

另外,可以利用Shell的#!机制,将progfile内容改为:
#!/usr/bin/awk –f

BEGIN{print "Output start!"};
{print};
END{print "Output done!"};

这样的话,执行./progfile awk_test.txt即可。(通过type awk可以知道系统中awk命令的位置。)

注:使用#!机制的话,执行的shell命令./progfile实际上是执行:"#!后面的命令" +"./progfile脚本" + "./progfile脚本的参数"。另外,这种写法,awk后面最多只能跟一个参数;并且ARGV[0]的值在不同系统上可能表现不同,比如可能被解释为awk或/usr/bin/awk或./progfile。

比较特别的,如果要在program内使用或打印单引号,可以用其ASCII码'\47'表示。或者把程序写在文件中,这样就不用担心单引号和program外围的单引号混淆的问题。注意,在单引号中,反斜杠后接一个字符,会被解释成这个字符的字面意思,即和不加反斜杠是一样的含义。

你也可以通过下面两种方法来打印单引号:

awk 'BEGIN { print "Here is a single quote <'"'"'>" }'

awk 'BEGIN { print "Here is a single quote<'\''>" }'

AWK选项

  •  -f program-file 执行文件中的程序,前面已讲过。

Program-file可以有多个,通常用来将通用的代码或函数做成库,以实现代码复用。

环境变量AWKPATH用来指定-f的搜索路径,如果不指定AWKPATH,则默认搜索“.:/usr/local/share/awk”,可以通过修改AWKPATH或ENVIRON["AWKPATH"]来修改搜索路径,每个路径之间用冒号隔开(.::都可以表示当前路径)。如果-f选项后面跟的是包含“/”的文件名,就不会去额外搜索路径了。

  •  -v var=value 变量赋值,在BEGIN之前进行。
例如,定义变量name,并赋值为“jason”:
[root@ubuntu]awk_test:$ awk -v name=jason 'BEGIN{printf("name=%s\n", name)}'
name=jason

注意变量的引用不需要加“$”,实际上AWK的语法很多跟ANSI C的语法类似。“$”在AWK中是用来引用field的,后面会讲到。

  •  -F fs 使用fs作为分隔符(默认是空格)
例如,打印/etc/passwd文件中的用户名一列:
[root@ubuntu]awk_test:$ cat /etc/passwd | awk -F ':' '{print $1}'
root
daemon
bin
sys
sync
games
  •  --compat 或 --traditional 使用原生awk,不会识别gawk的扩展。例如,原生awk允许在while/for/do循环外面使用continue和break语句,这会被认为和next语句意思相同。Gawk添加--traditional则可以使用这一特性。
  •  --dump-variables[=file] 输出排好序的AWK内置的全局变量到文件(若不指定文件,则默认为awkvars.out)

这个选项可以查看当前可用的内置全局变量。在编程的时候要注意,不要定义和这些内置变量重名的变量。

  •  --non-decimal-data 识别输入流中的十六进制和八进制
例如,将下面文件中的数字相加求和:
[root@ubuntu]awk_test:$ cat number.txt 
0x12
0x32
012
10

[root@ubuntu]awk_test:$ awk '{sum+=($1)}; END{print sum}' number.txt 
22
[root@ubuntu]awk_test:$ awk --non-decimal-data '{sum+=($1)}; END{print sum}' number.txt 
88

可以看到,如果不加--non-decimal-data选项,就只识别出十进制的12和10。

不过man gawk中说“Use this option with great caution!”,一方面这个选项可能破坏旧的程序,另一方面这个选项可能在以后被摒弃。

  •  --profile[=prof_file] 生成awk命令的profile文件
这个选项以优雅的格式将awk命令保存到文件,如果不指定文件名,则默认为awkprof.out。
[root@ubuntu]awk_test:$ awk --non-decimal-data --profile '{sum+=($1)}; END{print sum}' number.txt

[root@ubuntu]awk_test:$ cat awkprof.out 
	# gawk profile, created Sun Jan  8 23:20:48 2017

	# Rule(s)

	{
		sum += $1
	}

	# END block(s)

	END {
		print sum
	}

如果使用pgawk执行命令,则还会显示每条语句以及每个函数的调用次数。

  •  --re-interval 在正则表达式中支持间隔表达式

传统的awk不支持间隔表达式,必须加上--re-interval选项或者--posix选项才能使用。

  •  -e program-text --source program-text  program-text为awk的program源码,这个选项允许将文件中的源码和命令行中的源码混合使用。在需要引用自定义的库函数时就可以使用该选项,例如:

awk -f func_test.awk --source 'BEGIN{printadd_INT(1,2)}'  awk_test.txt

这个例子中,func_test.awk文件里面定义了函数add_INT(),这里将-f指定的文件程序和--source指定的命令行程序混合在了一起。

  •  -E file--exec file  意义和-f选项相同,不过有两点区别:命令行中的其他选项都直接传给awk,而awk先处理其他选项和参数,最后才处理--exec选项;另外,不允许“var=value”形式的变量赋值。

这个选项应该在#!开头的脚本里使用,例如:

 
#! /usr/local/bin/gawk -E
 
awk program here …

这个选项可以防止向脚本里传递参数,因为所有的参数都先被awk识别并处理了。

  • --include source-file
  • --load ext

这两个选项都是针对引用函数库的,也可以在文件中使用@include和@load来引用库文件。不过在我所用系统的gawk不支持这两个参数以及相应的AWKLIBPATH环境变量,我就不介绍了。我们就使用-f来引用库文件吧,只是-f的文件里面可以是任何程序内容,并不是只针对库文件而设计的。

  •  -- 标记选项的结束

这告诉awk,选项部分已经结束,可以用来传递以“-”开头的参数,而不被误认为是选项。

AWK的变量、Records和Fields

AWK的变量是动态生成的,在第一次被使用的时候开始存在。变量的值可以是下列数据类型:浮点型字符串类型一维数组。甚至一个变量既可以是浮点型也可以是字符串类型,这取决于代码中如何使用它。

AWK会将“var=value”形式的参数认为是变量赋值,例如下面这个命令,awk将“var=2”和“var=1”认为是给var赋值,而不是一个文件名,这个过程在awk顺序处理参数列表时进行的。

awk 'var == 1 {print 1} var == 2 { print 2}'  var=2 awk_test2.txt var=1 awk_test1.txt

Records

一个record就是awk认为的一行输入,对一个输入流,默认以换行符分隔。不过可以通过内置变量RS来修改。例如,把RS赋值为Jan07:

[root@ubuntu]awk_test:$ awk -v RS=Jan07 '{print}' awk_test.txt 
USER       PID %MEM    VSZ   RSS STAT START   TIME COMMAND
root         1  0.1   3652  1916 Ss   
   0:03 /sbin/init
root         2  0.0      0     0 S    
   0:00 [kthreadd]
root         3  0.
  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值