Linux|linux命令---GNU awk介绍

概述 ??gawk是GNU工程,是一种编程语言,它实现了标准awk的所有功能,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入(stdin)、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。awk有很多内建的功能,比如数组、函数等,这是它和C语言的相同之处,灵活性是awk最大的优势。其它 GNU/Linux 工具一样,awk 非常强大,而且只用到十分简单的编程语言。 它仅仅需要几行代码就能够完成复杂的文本处理工作。awk是行处理器,相比较屏幕处理的优点,在处理庞大文件时不会出现内存溢出或是处理缓慢的问题,通常用来格式化文本信息。
【Linux|linux命令---GNU awk介绍】本文是基于版本:GNU Awk 3.1.7
后续为了描述方便直接用关键字awk代替gawk,但是要清楚这里讲的是GNU awk而非标准的awk(会有一些扩展的使用会使用gawk命令)
工作流程 awk 执行的流程非常简单:读、执行、重复:
1、读:awk 从输入流(文件、管道或者标准输入)中读入一行然后将其存入内存中。
2、执行:对于每一行输入,所有的 awk 命令按顺执行。 默认情况下,awk 命令是针对于每一行输入,但是我们可以将其限制在指定的模式中。
??1)执行BEGIN{ commands }语句块中的语句;
??2)从文件或标准输入(stdin)读取一行,然后执行pattern{ commands }语句块,
??3)它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
3、当读至输入流末尾时,执行END{ commands }语句块。
注意
??BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中。
??pattern语句块中的通用命令是最重要的部分,它也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块
??END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块。
awk启程 基本语法

awk [ POSIX or GNU style options ] -f program-file [ -- ] file ... awk [ POSIX or GNU style options ] [ -- ] program-text file ...

选项 对应长格式的选项省略请自行对照man帮助手册
选项 说明
-F fs 指定输入文件折分隔符,fs是一个字符串或者是一个正则表达式,如-F:。
-f scripfile awk命令从文件中读入而不是从命令行输入,可以使用多个-f选项
-v var=value 赋值一个用户定义变量,将外部变量传递给awk
-mf nnn and -mr nnn 对nnn值设置内在限制,-mf选项限制分配给nnn的最大块数目;-mr选项限制记录的最大数目。这两个功能是Bell实验室版awk的扩展功能。在gawk中被忽略,因为在gawk中没有预定义限制。
-W compact 在兼容模式下运行awk。在兼容模式下awk的行为和标准的awk完全一样,所有的awk扩展都被忽略。
-W copyleft 打印简短的版权信息。
-W dump-variables[=file] 打印排序过的全局变量,并输入到文件,如果没有显式声明文件名,awk在当前目录中使用awkvars.out作为默认文件名
-W exec file 与-f类似,然而这个选项上最后一个被处理的。它被用在#!脚本中处理CGI应用:禁止从命令行中的URL中传入选项或者!。这个选项禁用命令行变量赋值。
-W gen-po AWK程序扫描和分析,把程序中所有本地化字符串在标准输出GNU .po格式文件。
-W help 打印全部awk选项和每个选项的简短说明。
-W lint 打印不能向传统unix平台移植的结构的警告。
-W lint-old 打印关于不能向传统unix平台移植的结构的警告。
-W non-decimal-data 输入的参数区别八进制和十六进制值。请谨慎使用这个选项!
-W posix 打开兼容模式。但有以下限制,不识别:\x、函数关键字、func、换码序列以及当fs是一个空格时,将新行作为一个域分隔符;操作符**和**=不能代替^和^=;fflush无效。
-W profile[=prof_file] 给prof_file分析数据。缺省值是awkprof.out。
-W re-interval 允许间隔正则表达式的使用,参考(grep中的Posix字符类),如括号表达式[[:alpha:]]。
-W source program-text 使用program-text作为源代码,可与-f命令混用。
-W version 打印版本信息。
awk程序执行 awk脚本是由一些列”pattern-ation”和可选自定义函数组成的:
patter{action statements} function name(parameter list) {statements}

pattern和ation是可选的,如果没有模式,则action应用到全部记录,如果没有action,则输出匹配全部记录。默认情况下,每一个输入行都是一条记录,但用户可通过RS变量指定不同的分隔符进行分隔。
模式
模式可以是以下任意一个:
  • 正则表达式:使用通配符的扩展集。
  • 关系表达式:可以用下面运算符表中的关系运算符进行操作,可以是字符
  • 串或数字的比较,如$2>%1选择第二个字段比第一个字段长的行。
  • 模式匹配表达式:用运算符~(匹配)和~!(不匹配)。
  • 模式,模式:指定一个行的范围。该语法不能包括BEGIN和END模式。
  • BEGIN:让用户指定在第一条输入记录被处理之前所发生的动作,通常可在这里设置全局变量。
  • END:让用户在最后一条输入记录被读取之后发生的动作。
操作
操作由一人或多个命令、函数、表达式组成,之间由换行符或分号隔开,并位于大括号内。主要有:
  • 变量或数组赋值
  • 输出命令
  • 内置函数
  • 控制流命令
这几部分会在后续的文档中分散介绍~
记录和域 记录
awk把每一个以换行符结束的行称为一个记录。
记录分隔符:默认的输入和输出的分隔符都是回车,保存在内建变量ORS和RS中。
$0变量:它指的是整条记录。
变量NR:一个计数器,每处理完一条记录,NR的值就增加1。

[root@wmstianjin16172 ~]# awk '{print NR,$0}' /etc/passwd 1 root:x:0:0:root:/root:/bin/bash 2 bin:x:1:1:bin:/bin:/sbin/nologin ……

将输出/etc/passwd文件中所有记录,并在记录前显示记录号。

记录中每个单词称做“域”,这些域是由内建变量FS指定分割符的,默认情况下以空格或tab分隔。awk可跟踪域的个数,并在内建变量NF中保存该值。如
[root@wmstianjin16172 ~]# awk -F':' '{print $1,$3}' /etc/passwd root 0 bin 1 ……

将打印/etc/passwd文件中第一和第三个以空格分开的列(域)。
域分隔符
内建变量FS保存输入域分隔符的值,默认是空格或tab。我们可以通过-F命令行选项修改FS的值。如
[root@wmstianjin16172 ~]# awk -F: '{print $1,$5}' /etc/passwd root root bin bin daemon daemon ……

将打印以冒号为分隔符的第一,第五列的内容。
可以同时使用多个域分隔符,这时应该把分隔符写成放到方括号中,如
[root@wmstianjin16172 ~]# awk -F'[:/t]' '{print $1,$3}' /etc/passwd roo x bin 1 ……

表示以空格、冒号和tab作为分隔符。
输出域的分隔符默认是一个空格,保存在OFS中。如
[root@wmstianjin16172 ~]# awk -F: '{print $1,$5}' /etc/passwd

$1和$5间的逗号就是OFS的值。
内置变量 说明:[A][N][P][G]表示第一个支持变量的工具,[A]=awk、[N]=nawk、[P]=POSIXawk、[G]=gawk
归属 属性 说明
$n 当前记录的第n个字段,比如n为1表示第一个字段,n为2表示第二个字段。$0这个变量包含执行过程中当前行的文本内容。
[N] ARGC 命令行参数的数目。
[G] ARGIND 命令行中当前文件的位置(从0开始算)。
[N] ARGV 包含命令行参数的数组。
[G] CONVFMT 数字转换格式(默认值为%.6g)。
[P] ENVIRON 环境变量关联数组。
[N] ERRNO 最后一个系统错误的描述。
[G] FIELDWIDTHS 字段宽度列表(用空格键分隔)。
[A] FILENAME 当前输入文件的名。
[P] FNR 同NR,但相对于当前文件。
[A] FS 字段分隔符(默认是任何空格)。
[G] LINT 对- -lint进行动态控制,当选项为true时gawk输出lint警告,当选项为false时不出警告。当设置为fatal时,lint警告升级为errors,任何其他的选项都被解析成true即打印warning信息
[G] IGNORECASE 如果为真,则进行忽略大小写的匹配。
[A] NF 表示字段数,在执行过程中对应于当前的字段数。
[A] NR 表示记录数,在执行过程中对应于当前的行号。
[A] OFMT 数字的输出格式(默认值是%.6g)。
[A] OFS 输出字段分隔符(默认值是一个空格)。
[A] ORS 输出记录分隔符(默认值是一个换行符)。
[G] PROCINFO 提供对运行的awk程序进行访问 procinfo
[A] RS 记录分隔符(默认是一个换行符)。
[N] RSTART 由match函数所匹配的字符串的第一个位置。
[N] RLENGTH 由match函数所匹配的字符串的长度。
[N] SUBSEP 数组下标分隔符(默认值是34)。
内置函数 算数函数
函数名 说明
atan2(y, x) 返回 y/x 的反正切。
cos(x) 返回 x 的余弦;x 是弧度。
sin(x) 返回 x 的正弦;x 是弧度。
exp(x) 返回 x 幂函数。
log(x) 返回 x 的自然对数。
sqrt(x) 返回 x 平方根。
int(x) 返回 x 的截断至整数的值。
rand() 返回任意数字 n,其中 0 <= n < 1。
srand([Expr]) 将 rand 函数的种子值设置为 Expr 参数的值,或如果省略 Expr 参数则使用某天的时间。返回先前的种子值。
字符串函数是
Functions&Functions Details
gsub(Ere, Repl, [ In ] ) 除了正则表达式所有具体值被替代这点,它和 sub 函数完全一样地执行。
sub( Ere, Repl, [ In ] ) 用 Repl 参数指定的字符串替换 In 参数指定的字符串中的由 Ere 参数指定的扩展正则表达式的第一个具体值。sub 函数返回替换的数量。出现在 Repl 参数指定的字符串中的 &(和符号)由 In 参数指定的与 Ere 参数的指定的扩展正则表达式匹配的字符串替换。如果未指定 In 参数,缺省值是整个记录($0 记录变量)。
index(str1, str2 ) 在由 str1 参数指定的字符串(其中有出现 str2 指定的参数)中,返回位置,从 1 开始编号。如果 str2 参数不在 str1 参数中出现,则返回 0(零)。
length [(str)] 返回 str 参数指定的字符串的长度(字符形式)。如果未给出 str 参数,则返回整个记录的长度($0 记录变量)。
blength [(str)] 返回 str 参数指定的字符串的长度(以字节为单位)。如果未给出 str 参数,则返回整个记录的长度($0 记录变量)。
substr(str, M, [ N ] ) 返回具有 N 参数指定的字符数量子串。子串从 str 参数指定的字符串取得,其字符以 M 参数指定的位置开始。M 参数指定为将 str 参数中的第一个字符作为编号 1。如果未指定 N 参数,则子串的长度将是 M 参数指定的位置到 str 参数的末尾 的长度。
match(str, Ere ) 在 str 参数指定的字符串(Ere 参数指定的扩展正则表达式出现在其中)中返回位置(字符形式),从 1 开始编号,或如果 Ere 参数不出现,则返回 0(零)。RSTART 特殊变量设置为返回值。RLENGTH 特殊变量设置为匹配的字符串的长度,或如果未找到任何匹配,则设置为 -1(负一)。
split( str, A, [Ere] ) 将 str 参数指定的参数分割为数组元素 A[1], A[2], …, A[n],并返回 n 变量的值。此分隔可以通过 Ere 参数指定的扩展正则表达式进行,或用当前字段分隔符(FS 特殊变量)来进行(如果没有给出 Ere 参数)。除非上下文指明特定的元素还应具有一个数字值,否则 A 数组中的元素用字符串值来创建。
tolower( str ) 返回 str 参数指定的字符串,字符串中每个大写字符将更改为小写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。
toupper( str ) 返回 str 参数指定的字符串,字符串中每个小写字符将更改为大写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。
sprintf(Format,Expr,Expr,…) 根据 Format 参数指定的 printf 子例程格式字符串来格式化 Expr 参数指定的表达式并返回最后生成的字符串。其中格式化字符串包括两部分内容: 一部分是正常字符, 这些字符将按原样输出; 另一部分是格式化规定字符, 以”%”开始, 后跟一个或几个规定字符,用来确定输出内容格式。格式符
一般函数
函数 说明
close( Expression) 用同一个带字符串值的 Expression 参数来关闭由 print 或 printf 语句打开的或调用 getline 函数打开的文件或管道。如果文件或管道成功关闭,则返回 0;其它情况下返回非零值。如果打算写一个文件,并稍后在同一个程序中读取文件,则 close 语句是必需的。
system(Command) 执行 Command 参数指定的命令,并返回退出状态。等同于 system 子例程。
Expression getline [ Variable ]
getline [Variable] 从 Expression 参数指定的文件读取输入的下一个记录,并将 Variable 参数指定的变量设置为该记录的值。只要流保留打开且 Expression 参数对同一个字符串求值,则对 getline 函数的每次后续调用读取另一个记录。如果未指定 Variable 参数,则 $0 记录变量和 NF 特殊变量设置为从流读取的记录。
getline [ Variable ] 将 Variable 参数指定的变量设置为从当前输入文件读取的下一个输入记录。如果未指定 Variable 参数,则 $0 记录变量设置为该记录的值,还将设置 NF、NR 和 FNR 特殊变量。
时间函数
函数名 说明
mktime( YYYY MM DD HH MM SS[ DST]) 生成时间格式
strftime([format [, timestamp]]) 格式化时间输出,将时间戳转为时间字符串 时间格式
systime() 得到时间戳,返回从1970年1月1日开始到当前时间(不计闰年)的整秒数
用户自定义函数
格式
function name(parameter list) { statements }

简单示例:做个加法
[root@wmstianjin16172 ~]# awk 'function fun(a,b){return a+b}BEGIN{print fun(1,2)}' 3

操作符
运算符 描述
(…)
= +=?-=?*=?/=?%=?^=?**= 赋值
?: C条件表达式
|| 逻辑或
&& 逻辑与
~ ~! 匹配正则表达式和不匹配正则表达式
?>=?!=?== 关系运算符
空格 连接
+?- 加,减
* / % 乘,除与求余
+ - ! 一元加,减和逻辑非
^ ** 求幂,也可参见**=
++ - - 增加或减少,作为前缀或后缀
$ 字段引用
in 数组成员
正则表达式 ERES
字符 功能
+ 指定如果一个或多个字符或扩展正则表达式的具体值(在 +(加号)前)在这个字符串中,则字符串匹配。
? 指定如果零个或一个字符或扩展正则表达式的具体值(在 ?(问号)之前)在字符串中,则字符串匹配。
| 指定如果以
( ) 在正则表达式中将字符串组合在一起
{m} 指定如果正好有 m 个模式的具体值位于字符串中,则字符串匹配。只有在指定–posix或者–re-interval的时候有效
{m,} 指定如果至少 m 个模式的具体值在字符串中,则字符串匹配。只有在指定–posix或者–re-interval的时候有效
{m, n} 指定如果 m 和 n 之间(包含的 m 和 n)个模式的具体值在字符串中(其中m<= n),则字符串匹配。只有在指定–posix或者–re-interval的时候有效
[str] 指定正则表达式与方括号内 str 变量指定的任何字符匹配。
[\^ str] 排除字符类。在 [ ](方括号)和在指定字符串开头的 ^ (插入记号) 指明正则表达式与方括号内的任何字符不匹配。[\^a-c]表示开头不是a、b、c任意一个字符
~,!~ 表示指定变量与正则表达式匹配(代字号)或不匹配(代字号、感叹号)的条件语句。
^ 指定字段或记录的开头。如[a-c]表示以a、b、c任意一个字符开头
$ 指定字段或记录的末尾。
. (句号) 表示除了在空白末尾的终端换行字符以外的任何一个字符。
*(星号) 表示零个或更多的任意字符。
\ (反斜杠) 转义字符。当位于在扩展正则表达式中具有特殊含义的任何字符之前时,转义字符除去该字符的任何特殊含义。
- 当符号不在第一个位置是,表示一个范围如[a-c]
一些例子:
[root@wmstianjin16174 ~]# awk '/ro+t/' /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin

[root@wmstianjin16174 ~]# awk '/root?/' /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin

[root@wmstianjin16172 ~]# awk '/^(a|b)/{print $0}'/etc/passwd bin:x:1:1:bin:/bin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin

[root@wmstianjin16172 ~]# awk '/[^a-c]/{print $0}'/etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin

[root@wmstianjin16172 ~]# awk --posix'/ro{2}t/{print $0}'/etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin

[root@wmstianjin16172 ~]# awk --posix'/ro{2,}t/{print $0}'/etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin

[root@wmstianjin16172 ~]# awk --posix'/ro{1,2}t/{print $0}'/etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin

[root@wmstianjin16172 ~]# awk'/r[ostx]ot/{print $0}'/etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin

[root@wmstianjin16172 ~]# awk '/r.ot/{print $0}'/etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin

[root@wmstianjin16172 ~]# awk '/r*t/{print $0}'/etc/passwd root:x:0:0:root:/root:/bin/bash shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt

[root@wmstianjin16172 ~]# awk '$0~/root/{print $0}'/etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin

[root@wmstianjin16172 ~]# awk '$0!~/root/{print $0}'/etc/passwd bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin

[root@wmstianjin16172 ~]# awk'/nologin$/{print $0}'/etc/passwd bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin

gawk专用
表达式 含义
\Y 匹配一个单词开头或者末尾的空字符串。
\B 匹配单词内的空字符串。
\< 匹配一个单词的开头的空字符串,锚定开始。
> 匹配一个单词的末尾的空字符串,锚定末尾。
\w 匹配一个字母数字组成的单词。
\W 匹配一个非字母数字组成的单词。
\‘ 匹配字符串开头的一个空字符串。
\’ 匹配字符串末尾的一个空字符串。
POSIX字符类补充
POSIX标准对正则表达式字符和操作符的含义进行了形式化。这种标准定义了两类正则表达式:基本的正则表达式(BRE),grep和sed使用这种正则表达式;扩展的正则表达式,egrep和awk使用这种正则表达式。
POSIX改变了称为”字符类”的东西,在POSIX标准中成为”括号表达式”。在括号表达式中,出了字面字符(例如a, !等)以外,还可以由其他标记。
  • 字符类。由[:和:]包围的关键字组成的POSIX字符类。关键字描述了不同的字符类,如文字字符类,控制字符等等。
  • 整理复合。整理复合是多字符的序列。表示这些字符应该被看作是一个单元,它由[,和.]包围的字符组成。
  • 等价类。等价类列出了应该看作是等价的字符集,它由地区化的字符元素(有[=和=]包围)组成。
POSIX字符类 含义
[:alnum:] 可打印的字符(包括空白字符)
[:alpha:] 字母字符
[:blank:] 空格和制表符
[:cntrl:] 控制字符
[:digit:] 数字字符
[:graph:] 可打印和可见的(非空格)字符
[:lower:] 小写字符
[:print:] 可打印的字符(包括空包字符)
[:punct:] 标点符号字符
[:space:] 空白字符
[:upper:] 大写字符
[:xdigit:] 十六进制数字
举个栗子,只举一个其他自己试验摸索(没新鲜东西如果上面的都懂了,玩转这类跟玩一样)
[root@wmstianjin16172 ~]# awk'/r[:alpha:]+t/{print $0}'/etc/passwd operator:x:11:0:operator:/root:/sbin/nologin sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin

数组 awk中的数组的下标可以是数字和字母,称为关联数组。数组是awk的灵魂,处理文本中最不能少的就是它的数组处理。因为数组索引(下标)可以是数字和字符串在awk中数组叫做关联数组(associative arrays)。awk 中的数组不必提前声明,也不必声明大小。数组元素用0或空字符串来初始化,这根据上下文而定。
定义方法
可以用数值作数组索引(下标)
[root@wmstianjin16172 ~]# awk 'BEGIN{arr[1]="first test"; print arr[1]}' first test

可以用字符串作数组索引(下标)
[root@wmstianjin16172 ~]# awk 'BEGIN{arr["first"]="first test"; print arr["first"]}' first test

数组相关函数
得到数组长度(length方法使用)
[root@wmstianjin16172 ~]# awk 'BEGIN{info="First arrTest"; lens=split(info,tmpArr," "); print length(tmpArr),lens; }' 2 2

length返回字符串以及数组长度,split进行分割字符串为数组,也会返回分割得到数组长度。
asort使用:
[root@wmstianjin16172 ~]# awk 'BEGIN{info="First arrTest"; lens=split(info,tmpArr," "); print asort(tmpArr); }' 2

asort对数组进行排序,返回数组长度。
输出数组内容(无序,有序输出):
[root@wmstianjin16172 ~]# awk 'BEGIN{info="First arrTest"; lens=split(info,tmpArr," "); for(var in tmpArr){print var,tmpArr[var]}; }' 1 First 2 arrTest

for…in 输出,因为数组是关联数组,默认是无序的。所以通过for…in 得到是无序的数组。如果需要得到有序数组,需要通过下标获得。
[root@wmstianjin16172 ~]# awk 'BEGIN{info="First arrTest"; lens=split(info,tmpArr," "); for(var=1; var<=lens; var++){print var,tmpArr[var]}; }' 1 First 2 arrTest

注意:数组下标是从1开始,与c数组不一样。
判断键值存在以及删除键值:
[root@wmstianjin16172 ~]# awk 'BEGIN{info="First arrTest"; lens=split(info,tmpArr," "); if("isExist" in tmpArr){print "exist key isExist"}else{print "not found key isExist"}for(var=1; var<=lens; var++){print var,tmpArr[var]}; }' not found key isExist 1 First 2 arrTest

if(key in array) 通过这种方法判断数组中是否包含”key”键值。
删除键值:
[root@wmstianjin16172 ~]# awk 'BEGIN{info="First arrTest"; lens=split(info,tmpArr," "); delete tmpArr[1]; for(var=1; var<=lens; var++){print var,tmpArr[var]}; }' 1 2 arrTest [root@wmstianjin16172 ~]# awk 'BEGIN{info="First arrTest"; lens=split(info,tmpArr," "); delete tmpArr[2]; for(var=1; var<=lens; var++){print var,tmpArr[var]}; }' 1 First 2

delete array[key]可以删除,对应数组key的,序列值。
二维数组使用(多维数组使用)
awk的多维数组在本质上是一维数组,更确切一点,awk在存储上并不支持多维数组。awk使用一个特殊的字符串SUBSEP (\034)作为分割字段,在上面的例子中,关联数组array存储的键值实际上是row\034col。
数组访问方式:
  • awk提供了逻辑上模拟二维数组的访问方式:array[row,col]
  • 多维数组可以使用 if ( (i,j) in array)这样的语法,但是下标必须放置在圆括号中。
  • 多维数组使用 for ( item in array )这样的语法遍历数组。与一维数组不同的是,多维数组必须使用split()函数来访问单独的下标分量。split ( item, subscr, SUBSEP)
通过arr[key1,key2]引用获得数组内容
[root@wmstianjin16172 ~]#awk 'BEGIN{for(i=1; i<=9; i++){for(j=1; j<=9; j++){tmpArr[i,j]=i\*j; print i,"\*",j,"=",tmpArr[i,j]; }}}' 1 * 1 = 1 1 * 2 = 2 …… 9 * 7 = 63 9 * 8 = 72 9 * 9 = 81

if(i,j)的使用
[root@wmstianjin16172 ~]# awk 'BEGIN{for(i=1; i<=9; i++){for(j=1; j<=9; j++){tmpArr[i,j]=i*j; }}for(i=1; i<=9; i++){for(j=1; j<=9; j++){if( (i,j) in tmpArr)print i"*"j"="tmpArr[i,j]; }}}' 1*1=1 1*2=2 …… 9*8=72 9*9=81

split()的使用
[root@wmstianjin16172 ~]# awk 'BEGIN{for(i=1; i<=9; i++){for(j=1; j<=9; j++){tmpArr[i,j]=i\*j; }}for(m in tmpArr){split(m,tmpArr2,SUBSEP); print tmpArr2[1],"*",tmpArr2[2],"=",tmpArr[m]; }}' 1 * 1 = 1 1 * 2 = 2 …… 9 * 7 = 63 9 * 8 = 72 9 * 9 = 81

输入输出指令 (指令中提到的协同进程可自行进阶,在此表下面我也给出了一些网友的资料不做过多讲解)
指令 解释
close(file[,how]) 关闭文件、管道或协同进程,参数how只有在关闭一个双向协同进程时才会被启用,而且只能”to”和”from”之一
getline 从输入中获取记录覆盖$0,即重新设置NF,NR,FNR
getline 从文件中读取记录覆盖$0,设置NF
getline var 使用输入的记录给变量var赋值,设置NR,FNR
getline var 从文件中读取记录给变量var赋值
command|getline [var] 执行命令,从命令的输出中读取记录(也可以赋值给var变量,同上)
command|\&getline [var] 执行命令作为一个协同进程并将输出覆盖$0或者存储到变量里,同上.co-processes是gawk扩展(命令可以是一个socket)
next 停止处理当前记录,按照最开始的模式重新开始处理下一条记录,若到达文件结尾,如有END程序块就会被执行
nextfile 停止处理当前文件,记录将会从下一个文件读取。FILENAME和ARGIND变量会被更新,FNR会被重置为1,并且会从最开始的模式重新开始处理记录。若到达文件结尾,如有END程序块就会被执行
print 打印当前记录,输出记录结尾是ORS的变量指定的值(输出记录分隔符)
print expr-list 打印,每个打印语句由OFS指定的值作为分割,每个记录由ORS指定的值作为分割
print expr-list >file 打印到文件,每个打印语句由OFS指定的值作为分割,每个记录由ORS指定的值作为分割
printf fmt, expr-list >file 格式化并输出到文件
system(cmd-line) 在控制台执行命令,并返回执行状态码(在非POSIX系统不可用)
fflush([file]) 刷新任何与打开输出文件或管道文件关联起来缓冲区。如果文件丢失,则刷新标准输出。如果文件是空字符串,则刷新所有文件和管道的输出缓冲区。另外print和printf支持输出重定向
print … >> file 追加输出到文件
print …| command 输出到管道
print …|& command 发送数据到co-process或者socket
?
协同进程(科普)
|& : 异步地处理前面的管道,并建立到父shell 的双向管道, shell依次执行每个命令,在后台处理管道而不等待它完成,通过使用 read -p 和 print -p 命令,父 shell 可以读取和写入衍生命令的标准输入和输出,在任何给定时刻,只能有一个这样的命令活动。
symbol |& causes asynchronous execution of the preceding command or pipeline with a two-way pipe established to the parent shell. The standard input and output of the spawned command can be written to and read from by the parent Shell using the -p option of the special commands read and print described later.
[root@test255150 ~]# cat co-processTest.sh #!/bin/ksh function processTest { while : do read funVar if [[ $funVar = "Y" ]] then return 777 fi done }processTest|& echo "后台进程号"$!."$! sleep 5 var="Y" print -p "$var"

co-process
协同进程(被KSH支持,bash不支持)
1、协同进程通过管道在标准输入上接受一个程序的数据,经过处理后再通过标准输出流经管道送给程序。
2、如果一个进程需要协同进程处理数据,步骤如下:
  • 1)创建两个管道,fd1[2]和fd2[2](父进程在fd1[1]写入数据,再从fd2[0]读出数据)。
  • 2)调用fork()创建子进程。
  • 3)在子进程中。关闭fd1[1], fd2[0],并调用dup2使协同进程的标准输入连接到fd1[0], 标准输出连接到fd2[1],这样就将两个管道连接起来了。再在子进程中调用execl调用编写的协同处理程序(这里的系统程序做为一般的程序编写即可,从标准输入读入数据,处理后从输出到标准输出)。
  • 4)在父进程中。关闭fd1[0], fd2[1]。将需要系统进程处理的数据写入fd1[1], 再从fd2[0]读出即可。
3、协同进程程序编写时需要注意的问题:协同进程中不能使用标准IO库进行输入输出。因为协同进程的标准输入输出是被连接到管道的,进行标准IO时系统默认是全缓冲类型,并为其分配缓冲区。由于全缓冲机制是只有缓冲区被填满才进行实际的IO输出,所以当输入缓冲区未填满时协同进程在其标准输入上读数据会发生阻塞,而协同进程的输出缓冲区为填满时,主进程在管道上读数据时也会发生阻塞,这样两个进程都阻塞了就发生了死锁。
控制指令 分支控制
if 语句 格式:
if (表达式){ //todo }

[root@wmstianjin16172 ~]# awk 'BEGIN{for(i=1; i<=9; i++)if(i%2==0)print i}' 2 4 6 8

if/else语句,用于双重判断 格式:
if (表达式){ //todo }else{ //todo }

[root@wmstianjin16172 ~]# awk 'BEGIN{for(i=1; i<=9; i++)if(i%2==0){print i; }else{print "*****"; }}' ***** 2 ***** 4 ***** 6 ***** 8 *****

if/else else if语句,用于多重判断。 格式:
if (表达式){ //todo }else if (表达式){ //todo }else if (表达式){ //todo }else { //todo }

[root@wmstianjin16172 ~]# awk 'BEGIN{for(i=1; i<=9; i++)if(i%2==0){print i; }else if(i%3==0){print "***"; }else{print "@@@@@"}}' @@@@@ 2 *** 4 @@@@@ 6 @@@@@ 8 ***

循环控制
awk有四种循环: while循环;for循环;special for循环;do……while循环。
while 格式
while (表达式) { //todo }

[root@wmstianjin16172 ~]# awk 'BEGIN{i=1; while(i<=9){if(i%2==0){print i; }else if(i%3==0){print "***"; }else{print "@@@@@"}i++}}' @@@@@ 2 *** 4 @@@@@ 6 @@@@@ 8 ***

$ awk ‘{ i = 1; while ( i <= NF ) { print NF,$i; i++}}’ test。变量的初始值为1,若i小于可等于NF(记录中域的个数),则执行打印语句,且i增加1。直到i的值大于NF.
for 格式
for (变量; 条件; 表达式){ //todo } 或者 for(变量 in 数组) { //todo }

[root@wmstianjin16172 ~]# awk 'BEGIN{for(i=1; i<=9; i++)if(i%2==0){print i; }else if(i%3==0){print "***"; }else{print "@@@@@"}}' @@@@@ 2 *** 4 @@@@@ 6 @@@@@ 8 *** [root@wmstianjin16172 ~]# awk 'BEGIN{a[1]=1; a[2]=2; a[3]=3; for(i in a)if(i%2==0){print i; }else if(i%3==0){print "***"; }else{print "@@@@@"}}' @@@@@ 2 ***

do……while 格式
do{ //todo } while(表达式)

[root@wmstianjin16172 ~]# awk 'BEGIN{i=1; do{if(i%2==0){print i; }else if(i%3==0){print "***"; }else{print "@@@@@"}i++}while(i<=9)}' @@@@@ 2 *** 4 @@@@@ 6 @@@@@ 8 ***

  • break 当 break 语句用于 while 或 for 语句时,导致退出程序循环。
  • continue 当 continue 语句用于 while 或 for 语句时,使程序循环移动到下一个迭代。
  • next 能能够导致读入下一个输入行,并返回到脚本的顶部。这可以避免对当前输入行执行其他的操作过程。
  • exit 语句使主输入循环退出并将控制转移到END,如果END存在的话。如果没有定义END规则,或在END中应用exit语句,则终止脚本的执行。
对于这几种循环的性能可自行比较一下,在此我就介绍下基本用法。提示一下用time命令测试
实例 1、print
是awk打印指定内容的主要命令,可以输出内置变量、用户自定义变量,常量、空输出(作为换行符)等,print不跟参数表示输出$0
[root@wmstianjin16172 ~]# awk -v a="userDefine" 'BEGIN{print ""; print "const"; print a; print NF}'const userDefine 0

这里顺便用了一下-v传参~,-v不再单独举例
2、-f指定脚本文件
[root@wmstianjin16172 ~]# cat awkFile.awk #!/bin/awk BEGIN{print; print "const"; print a; print NF} [root@wmstianjin16172 ~]# awk -v a="userDefine" -f awkFile.awk const userDefine 0

3、-F指定分隔符(可以指定多个)
$n:n指非负整数(但是你可以输入任何字符,找不到认为是0),0代表是整个字段,1~n为以F分隔符分割的字段域。
[root@wmstianjin16172 ~]# echo "x1 x2 x3 x4:x5"|awk '{print $0; print $notExist}' x1 x2 x3 x4:x5 x1 x2 x3 x4:x5 [root@wmstianjin16172 ~]# echo "x1 x2 x3 x4:x5"|awk -F':' '{print $0; print $1}' x1 x2 x3 x4:x5 x1 x2 x3 x4

-F指定多个分隔符需要用[]括起来,否则认为多个字符是一个整体:
[root@wmstianjin16172 ~]# awk -F'[:/]' '{print NR,$2}' /etc/passwd 1 x 2 x 3 x ……[root@wmstianjin16172 ~]# awk -F':/' '{print NR,$2}' /etc/passwd 1 root 2 bin 3 sbin ……

4、正则匹配和不匹配
/正则表达式或纯字符/ 字符匹配
[root@wmstianjin16172 ~]# awk -F":" '/root/{print $1,$0}' /etc/passwd root root:x:0:0:root:/root:/bin/bash operator operator:x:11:0:operator:/root:/sbin/nologin

!/正则表达式或纯字符/字符不匹配
[root@wmstianjin16172 ~]# awk -F":" '!/root/{print $1,$0}' /etc/passwd bin bin:x:1:1:bin:/bin:/sbin/nologin daemon daemon:x:2:2:daemon:/sbin:/sbin/nologin ……

~/正则表达式或纯字符/:字段值匹配,形式为var~/正则表达式或纯字符/
[root@wmstianjin16172 ~]# awk -F":" '$1~/root/{print $1,$0}' /etc/passwd root root:x:0:0:root:/root:/bin/bash

!~/正则表达式或纯字符/:字段值不匹配形式为var!~/正则表达式或纯字符/
[root@wmstianjin16172 ~]# awk -F":" '$1!~/root/{print $1,$0}' /etc/passwd bin bin:x:1:1:bin:/bin:/sbin/nologin daemon daemon:x:2:2:daemon:/sbin:/sbin/nologin ……

5、~/str1|str2/:字段值匹配st1或str2 ,形式为var~/str1|str2/
[root@wmstianjin16172 ~]# awk -F":" '$1~/root|tomcat/{print $1,$0}' /etc/passwd root root:x:0:0:root:/root:/bin/bash tomcat tomcat:x:1128:1128::/home/tomcat:/bin/bash

6、关系运算符:==、!=、>、>=、<、<=
第一个域为root(==):
[root@wmstianjin16172 ~]# awk -F":" '$1=="root"{print $1,$0}' /etc/passwd root root:x:0:0:root:/root:/bin/bash [root@wmstianjin16172 ~]# awk -F":" '{if($1=="root") print $1,$0}' /etc/passwd root root:x:0:0:root:/root:/bin/bash

第一个域不为root(!=):
[root@wmstianjin16172 ~]# awk -F":" '$1!="root"{print $1,$0}' /etc/passwd bin bin:x:1:1:bin:/bin:/sbin/nologin daemon daemon:x:2:2:daemon:/sbin:/sbin/nologin ……

第三个域大于1000(>)
[root@wmstianjin16172 ~]# awk -F":" '$3>1000{print $0}' /etc/passwd zabbix:x:1127:1127::/home/zabbix:/bin/bash ……

<、>=、<=类似不在赘述
7、逻辑运算符&&、||
逻辑与,$1匹配tomcat,并且$3>1000
awk -F: '\$1~/tomcat/ && \$3>1000 {print }' /etc/passwd

逻辑或,$1匹配root,或者$3>1000
[root@wmstianjin16172 ~]# awk -F: '$1~/root/ || $3>1000 {print }' /etc/passwd root:x:0:0:root:/root:/bin/bash zabbix:x:1127:1127::/home/zabbix:/bin/bash tomcat:x:1128:1128::/home/tomcat:/bin/bash spotlight:x:1129:1129::/home/spotlight:/bin/bash

8、数值运算
[root@wmstianjin16172 ~]# echo "3 4"|awk -F" " '{print "a+b="$1+$2"\ta-b="$1-$2"\ta/b="$1/$2"\ta*b="$1*$2}' a+b=7a-b=-1a/b=0.75a*b=12

9、数学函数
[root@wmstianjin16172 ~]# awk 'BEGIN{print atan2(1,2),cos(1),sin(0),exp(9),log(10),sqrt(9),int(1.444),rand()}' 0.463648 0.540302 0 8103.08 2.30259 3 1 0.237788

10、字符串操作
gsub(r,s) 在整个$0中用s替代r
[root@wmstianjin16172 ~]# awk -F: 'gsub(/^root/,"lingyue") {print $0}' /etc/passwd lingyue:x:0:0:root:/root:/bin/bash

index(s,t) 返回s中字符串t的第一位置
[root@wmstianjin16172 ~]# awk '/^root/{print index($0, ":")}' /etc/passwd 5

length(s) 返回s长度
[root@wmstianjin16172 ~]# awk '/^root/{print length($0)}' /etc/passwd 31

match(s,r) 测试s是否包含匹配r的字符串
[root@wmstianjin16172 ~]# awk '/^root/{print match($0,root)}' /etc/passwd 1

split(s,a,fs) 在fs上将s分成序列a,并返回长度
[root@wmstianjin16172 ~]# awk '/^root/{print split($0,myarray,":")}' /etc/passwd 7

sprintf(format,exp) 返回经format格式化后的exp
[root@wmstianjin16172 ~]# awk 'BEGIN{ t=sprintf("sprintf %d",10) ; print t }' sprintf 10

sub(r,s) 用$0中最左边最长的子串代替s,使用sub发现并替换模式的第一次出现位置
[root@wmstianjin16172 ~]# awk 'sub(/root/,"lingyue")' /etc/passwd lingyue:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/lingyue:/sbin/nologin

substr(s,p) 返回字符串s中从p开始的后缀部分
[root@wmstianjin16172 ~]# awk '/^root/{print substr($0,5)}' /etc/passwd :x:0:0:root:/root:/bin/bash

substr(s,p,n) 返回字符串s中从p开始长度为n的后缀部分
[root@wmstianjin16172 ~]# awk '/^root/{print substr($0,1,5)}' /etc/passwd root:

大小写转换
[root@wmstianjin16172 ~]# awk 'BEGIN{ print toupper("test"), tolower("TEST") }' TEST test

11、内置变量
输出一些默认值
[root@wmstianjin16172 ~]# awk -v a="xxx" 'BEGIN{FS=":"}/^root/{print "ARGC="ARGC,"ARGIND="ARGIND; for(argv in ARGV)print "ARGV["argv"]="ARGV[argv]; for(env in ENVIRON)print "ENVIRON["env"]="ENVIRON[env]; print "ERROR="ERRNO; print "FIELDWIDTHS="FIELDWIDTHS; print "FILENAME="FILENAME; print "FNR="FNR; print "IGNORECASE="IGNORECASE; print "NF="NF; print "NR="NR}' /etc/passwd ARGC=2 ARGIND=1 ARGV[0]=awk ARGV[1]=/etc/passwd ENVIRON[TERM]=xterm ……

按宽度指定分隔符(FIELDWIDTHS使用)
[root@wmstianjin16172 ~]# echo 20170725104932 | awk 'BEGIN{FIELDWIDTHS="4 2 2 2 2 3"}{print $1"-"$2"-"$3,$4":"$5":"$6}' 2017-07-25 10:49:32

输出数据格式设置:(OFMT使用)
[root@wmstianjin16172 ~]# awk 'BEGIN{OFMT="%.3f"; print 2/3,123.11111111; }' 0.667 123.111

RSTART RLENGTH使用
[root@wmstianjin16172 ~]# awk 'BEGIN{start=match("this is a test",/[a-z]+$/); print start, RSTART, RLENGTH }' 11 11 4 [root@wmstianjin16172 ~]# awk 'BEGIN{start=match("this is a test",/^[a-z]+$/); print start, RSTART, RLENGTH }' 0 0 -1

输出分隔符OFS,定义输出分割符为tab
[root@wmstianjin16172 ~]# awk -F':' '{print NR,$1,$2,$3}' OFS="\t" /etc/passwd 1rootx0 2bin x1 3daemonx2 ……

输出记录分割符ORS
[root@wmstianjin16172 ~]# awk -F':' '{print NR,$1,$2,$3}' OFS="\t" ORS="MM" /etc/passwd 1rootx0MM2bin x1MM3daemonx2MM4adm x3MM5lpx4MM6syncx5MM7shutdownx6MM8haltx7MM9 mailx8MM10uucpx10MM11operatorx11

12、赋值语句
[root@wmstianjin16172 ~]# awk 'BEGIN{a=100; b=a%7; print b; b+=5; print b; b-=1; print b; b*=4; print b; b/=3; print b; b^=2; print b; b**=2; print b}' 2 7 6 24 8 64 4096

13、sprintf示例
awk 'BEGIN{n1=124.113; n2=-1.224; n3=1.2345; printf("%.2f,%.2u,%.2g,%X,%o\n",n1,n2,n3,n1,n1); }' 124.11,18446744073709551615,1.2,7C,174

14、一般函数
getline和close函数
[root@wmstianjin16172 ~]# awk 'BEGIN{while("cat /etc/passwd"|getline){print $0; }; close("/etc/passwd"); }' root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin …… 或者 [root@wmstianjin16172 ~]# awk 'BEGIN{while(getline < "/etc/passwd"){print $0; }; close("/etc/passwd"); }' root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin ……

getline还可以实现交互
[root@wmstianjin16172 ~]# awk 'BEGIN{print "input arg:"; getline arg; print arg; }' input arg: hello world hello world

system调用外部应用程序
[root@wmstianjin16172 ~]# awk 'BEGIN{fileList=system("ls -al /tmp"); print fileList; }' total 56 drwxrwxrwt.4 root root4096 Jul 25 10:58 . dr-xr-xr-x. 26 root root4096 May 17 17:55 .. -rw-r--r--1 root root 30730 Jul 25 04:01 CutHttpLogEveryday.log drwxr-xr-x2 root root4096 Jul 24 14:52 hsperfdata_root drwxrwxrwt2 root root4096 May 11 02:38 .ICE-unix ……

15、时间函数
mktime创建指定时间
[root@wmstianjin16172 ~]# awk 'BEGIN{tstamp=mktime("2017 07 25 10 10 10"); print strftime("%c",tstamp); }' Tue 25 Jul 2017 10:10:10 AM CST

systime
[root@wmstianjin16172 ~]# awk 'BEGIN{timeStamp=systime(); print timeStamp}' 1500951774

[root@wmstianjin16172 ~]# awk 'BEGIN{ now=strftime( "%D",systime() ); print now }' 07/25/17

附录 strftime日期和时间格式说明符
格式 描述
%a 星期几的缩写(Sun)
%A 星期几的完整写法(Sunday)
%b 月名的缩写(Oct)
%B 月名的完整写法(October)
%c 本地日期和时间
%d 十进制日期
%D 日期 08/20/99
%e 日期,如果只有一位会补上一个空格
%H 用十进制表示24小时格式的小时
%I 用十进制表示12小时格式的小时
%j 从1月1日起一年中的第几天
%m 十进制表示的月份
%M 十进制表示的分钟
%p 12小时表示法(AM/PM)
%S 十进制表示的秒
%U 十进制表示的一年中的第几个星期(星期天作为一个星期的开始)
%w 十进制表示的星期几(星期天是0)
%W 十进制表示的一年中的第几个星期(星期一作为一个星期的开始)
%x 重新设置本地日期(08/20/99)
%X 重新设置本地时间(12:00:00)
%y 两位数字表示的年(99)
%Y 当前月份
%Z 时区(PDT)
%% 百分号(%)
sprintf格式符
格式符 说明
%d 十进制有符号整数
%u 十进制无符号整数
%f 浮点数
%s 字符串
%c 单个字符
%p 指针的值
%e 指数形式的浮点数
%x %X 无符号以十六进制表示的整数
%o 无符号以八进制表示的整数
%g 自动选择合适的表示法
PROCINFO元素
PROCINFO元素 解释
PROCINFO[“egid”] 系统调用getegid的值
PROCINFO[“euid”] 系统调用geteuid的值
PROCINFO[“FS”] 如果字段使用FS分割值是”FS”,若是使用FIELDWIDTHS分割值是”FIELDWIDTHS”
PROCINFO[“gid”] 系统调用getgid的值
PROCINFO[“pgrpid”] 当前进程组ID
PROCINFO[“pid”] 当前进程id
PROCINFO[“ppid”] 当前进程的父进程id
PROCINFO[“uid”] 系统调用getuid的值
PROCINFO[“version”] gawk版本,在3.1.4+可用
原文连接
篇幅有点长,难免有错~如果看到请不要留情面直接点出来^_^

    推荐阅读