AWK命令入门
AWK命令可追溯到Unix早期。
它是POSIX标准的一部分,应该在任何类似Unix的系统上可用。
AWK教程示例文件
该文章中描述的所有单行代码都将在同一数据文件上进行测试:
cat file CREDITS,EXPDATE,USER,GROUPS 99,01 jun 2016,sylvain,team:::admin 52,01 dec 2016,sonia,team 52,01 dec 2016,sonia,team 25,01 jan 2019,sonia,team 10,01 jan 2019,sylvain,team:::admin 8,12 jun 2016,öle,team:support 17,05 apr 2019,abhishek,guest
知道AWK中的预定义变量和自动变量
AWK支持几个预定义和自动变量,以编写程序。
其中,我们将经常遇到:
RS –记录分隔符。
AWK一次处理一条记录的数据。
记录分隔符是用于将输入数据流拆分为记录的定界符。
默认情况下,这是换行符。
因此,如果不进行更改,则记录是输入文件的一行。
NR –当前输入记录号。
如果我们使用标准换行符分隔符作为记录,则此匹配符将与当前输入行号匹配。
FS/OFS –用作字段分隔符的字符。
AWK读取记录后,会根据FS
的值将其分为不同的字段。
当AWK在输出上打印记录时,它将重新加入字段,但这一次,使用OFS分隔符而不是FS分隔符。
通常,“ FS”和“ OFS”是相同的,但这不是强制性的。
“空白”是它们两个的默认值。
NF –当前记录中的字段数。
如果我们为字段使用标准的“空白”定界符,则该定界符将与当前记录中的单词数匹配。
还有其他或者多或者少的标准AWK变量可用,因此值得检查特定AWK实施手册以获取更多详细信息。
但是,此子集已经足以开始编写有趣的单行代码。
A. AWK命令的基本用法
1.打印所有行
该示例几乎没有用,但是仍然可以很好地介绍AWK语法:
awk '1 { print }' file CREDITS,EXPDATE,USER,GROUPS 99,01 jun 2016,sylvain,team:::admin 52,01 dec 2016,sonia,team 52,01 dec 2016,sonia,team 25,01 jan 2019,sonia,team 10,01 jan 2019,sylvain,team:::admin 8,12 jun 2016,öle,team:support 17,05 apr 2019,abhishek,guest
AWK程序由一个或者多个pattern {action}
语句组成。
如果对于输入文件的给定记录(“行”),模式评估为非零值(相当于AWK中的“ true”),则执行相应动作块中的命令。
在上面的示例中,由于“ 1”是一个非零常量,因此将对每个输入记录执行“ {print}”操作块。
另一个窍门是“ {print}”是AWK将使用的默认操作块,如果我们未明确指定它的话。
因此,以上命令可以简化为:
awk 1 file CREDITS,EXPDATE,USER,GROUPS 99,01 jun 2016,sylvain,team:::admin 52,01 dec 2016,sonia,team 52,01 dec 2016,sonia,team 25,01 jan 2019,sonia,team 10,01 jan 2019,sylvain,team:::admin 8,12 jun 2016,öle,team:support 17,05 apr 2019,abhishek,guest
以下AWK程序几乎无用,它将消耗其输入,但不会对输出产生任何影响:
awk 0文件
2.删除文件头
awk 'NR>1' file 99,01 jun 2016,sylvain,team:::admin 52,01 dec 2016,sonia,team 52,01 dec 2016,sonia,team 25,01 jan 2019,sonia,team 10,01 jan 2019,sylvain,team:::admin 8,12 jun 2016,öle,team:support 17,05 apr 2019,abhishek,guest
请记住,这等效于显式编写:
awk 'NR>1 { print }' file 99,01 jun 2016,sylvain,team:::admin 52,01 dec 2016,sonia,team 52,01 dec 2016,sonia,team 25,01 jan 2019,sonia,team 10,01 jan 2019,sylvain,team:::admin 8,12 jun 2016,öle,team:support 17,05 apr 2019,abhishek,guest
该单行代码将写入输入文件的记录,但第一个记录除外,因为在这种情况下,条件是“ 1> 1”,这显然是不正确的。
由于此程序使用的是RS的默认值,因此实际上它将丢弃输入文件的第一行。
3.打印范围内的行
这只是前面示例的概括,它不需要进行很多解释,只是说“ &&”是逻辑“ and”运算符:
awk 'NR>1 && NR < 4' file 99,01 jun 2016,sylvain,team:::admin 52,01 dec 2016,sonia,team
4.删除仅空白行
awk 'NF' file CREDITS,EXPDATE,USER,GROUPS 99,01 jun 2016,sylvain,team:::admin 52,01 dec 2016,sonia,team 52,01 dec 2016,sonia,team 25,01 jan 2019,sonia,team 10,01 jan 2019,sylvain,team:::admin 8,12 jun 2016,öle,team:support 17,05 apr 2019,abhishek,guest
AWK根据FS
变量中指定的字段分隔符将每个记录分成多个字段。
默认的字段分隔符是一个或者几个白色空格字符(又称空格或者制表符)。
使用这些设置,任何包含至少一个非空白字符的记录将至少包含一个字段。
换句话说,“ NF”为0(“假”)的唯一情况是记录中仅包含空格。
因此,单线只打印至少包含一个非空格字符的记录。
5.删除所有空白行
awk '1' RS='' file CREDITS,EXPDATE,USER,GROUPS 99,01 jun 2016,sylvain,team:::admin 52,01 dec 2016,sonia,team 52,01 dec 2016,sonia,team 25,01 jan 2019,sonia,team 10,01 jan 2019,sylvain,team:::admin 8,12 jun 2016,öle,team:support 17,05 apr 2019,abhishek,guest
这种单线基于模糊的POSIX规则,该规则指定是否将RS设置为空字符串,“然后,记录由由<newline>加上一个或者多个空行组成的序列分隔。
”
值得一提的是POSIX术语,空行是完全空的行。
仅包含空格的行不算作“空白”。
6.提取字段
这可能是AWK最常见的用例之一:提取数据文件的某些列。
awk '{ print , }' FS=, OFS=, file CREDITS,USER 99,sylvain 52,sonia 52,sonia 25,sonia 10,sylvain 8,öle , , , 17,abhishek
其中我将输入字段分隔符和输出字段分隔符明确设置为逗号。
当AWK将记录拆分为多个字段时,它将第一个字段的内容存储为$1,第二个字段的内容存储为$2,依此类推。
我在这里不使用它,但是值得一提的是整个记录。
在这一单行代码中,我们可能已经注意到我使用了没有模式的动作块。
在这种情况下,该模式假定为1(“真”),因此将对每个记录执行操作块。
根据需求,它可能无法产生我们希望空白行或者仅空白行的内容。
在这种情况下,第二个版本可能会更好一些:
awk 'NF { print , }' FS=, OFS=, file CREDITS,USER 99,sylvain 52,sonia 52,sonia 25,sonia 10,sylvain 8,öle , 17,abhishek
在这两种情况下,我都在命令行中传递了“ FS”和“ OFS”的自定义值。
另一种选择是在读取第一条记录之前,在AWK程序中使用特殊的“ BEGIN”块来初始化这些变量。
因此,根据喜好,我们可能更愿意编写以下代码:
awk 'BEGIN { FS=OFS="," } NF { print , }' file CREDITS,USER 99,sylvain 52,sonia 52,sonia 25,sonia 10,sylvain 8,öle , 17,abhishek
值得一提的是,在读取最后一条记录后,我们还可以使用END
块来执行某些任务。
就像我们刚才看到的一样。
话虽这么说,我承认这还远非完美,因为纯空白行没有得到很好的处理。
我们很快就会看到可能的解决方案,但是在此之前,我们先做一些数学运算……
7.按列执行计算
AWK支持标准算术运算符。
并且会根据上下文自动在文本和数字之间转换值。
另外,我们可以使用自己的变量来存储中间值。
所有这些使我们可以编写紧凑的程序来对数据列执行计算:
awk '{ SUM=SUM+ } END { print SUM }' FS=, OFS=, file 263
或者,等效地使用+ =
速记语法:
awk '{ SUM+= } END { print SUM }' FS=, OFS=, file 263
请注意,使用前无需声明AWK变量。
假定未定义的变量可容纳空字符串。
根据AWK类型转换规则,该值等于0。
由于这个特性,我不必费心地处理$1
包含文本(在标题中),空格或者什么都不包含的情况。
在所有这些情况下,它都将计为0,并且不会干扰我们的求和。
当然,如果我执行乘法运算会有所不同。
那么,为什么不使用注释部分为该情况提供解决方案呢?
8.计算非空行数
我之前已经提到过“ END”规则。
这是另一个可能的应用程序,用于计算文件中非空行的数量:
awk '/./{ COUNT+=1 } END { print COUNT }' file 9
其中我使用了COUNT变量,并在与正则表达式/./
相匹配的每一行中将其递增(+ = 1
)。
那就是每一行至少包含一个字符。
最后,在处理完整个文件后,将使用END块显示最终结果。
名称COUNT没有什么特别的。
我本可以使用Count
,count
,n
,xxxx
或者其他任何符合AWK变量命名规则的名称
但是,这个结果正确吗?
好吧,这取决于我们对“空”行的定义。
如果我们认为仅空行(根据POSIX)为空,那么这是正确的。
但是,也许我们更希望将仅空白行也视为空行?
awk 'NF { COUNT+=1 } END { print COUNT }' file 8
这次的结果有所不同,因为更高版本也忽略了仅空白行,而初始版本仅忽略了空白行。
你能看到区别么?
我让你自己想一想。
如果不够清楚,请立即使用注释部分!
最后,如果我们只对数据行感兴趣,并给出了特定的输入数据文件,则可以这样写:
awk '+ { COUNT+=1 } END { print COUNT }' file 7
由于AWK类型转换规则,因此可以工作。
模式中的一元加号会强制在数值上下文中评估$1.
在我的文件中,数据记录的第一个字段中包含一个数字。
非数据记录(标题,空白行,仅空白行)不包含文本,也不包含任何内容。
当转换为数字时,它们都等于0。
注意,使用该最新解决方案,最终拥有0个积分的用户的记录也将被丢弃。
B.在AWK中使用数组
数组是AWK的强大功能。
AWK中的所有数组都是关联数组,因此它们允许将任意字符串与另一个值关联。
如果我们熟悉其他编程语言,则可能将它们称为哈希,关联表,字典或者映射。
9. AWK数组的一个简单示例
假设我想知道所有用户的总功劳。
我可以将每个用户的条目存储在关联数组中,每次遇到该用户的记录时,我都会增加存储在数组中的相应值。
awk '+ { CREDITS[]+= } END { for (NAME in CREDITS) print NAME, CREDITS[NAME] }' FS=, file abhishek 17 sonia 129 öle 8 sylvain 109
我承认这已不再是一线纸了。
主要是因为处理文件后,for
循环用于显示数组的内容。
因此,让我们现在回到更短的示例:
10.使用AWK识别重复的行
就像其他AWK变量一样,数组既可以在动作块中也可以在模式中使用。
充分利用这一点,我们可以编写单行代码以仅打印重复的行:
awk 'a[awk '!a[]++' file 52,01 dec 2016,sonia,teamawk '=' FS=, OFS=';' file CREDITS;EXPDATE;USER;GROUPS 99;01 jun 2016;sylvain;team:::admin 52;01 dec 2016;sonia;team 52;01 dec 2016;sonia;team 25;01 jan 2019;sonia;team 10;01 jan 2019;sylvain;team:::admin 8;12 jun 2016;öle;team:support 17;05 apr 2019;abhishek;guest]++' file CREDITS,EXPDATE,USER,GROUPS 99,01 jun 2016,sylvain,team:::admin 52,01 dec 2016,sonia,team 25,01 jan 2019,sonia,team 10,01 jan 2019,sylvain,team:::admin 8,12 jun 2016,öle,team:support 17,05 apr 2019,abhishek,guest
++运算符是从C语言家族继承的后递增运算符(由于Brian Kernighan是其最初的作者之一,AWK是一个引以为傲的成员)。
顾名思义,后递增运算符会将变量递增(“加1”),但仅在取其值用于评估包络表达式时才递增。
在这种情况下,将评估a [$0]以查看记录是否将被打印,并且一旦做出决定,在所有情况下,数组条目都会增加。
因此,第一次读取记录时,“ a [$0]”是未定义的,因此对于AWK等于零。
因此,该第一条记录不会写在输出上。
然后,该条目从零更改为一。
第二次读取相同的输入记录时,“ a [$0]”现在为1.
这是“ true”。
该行将被打印。
但是,在此之前,数组条目从1更新为2.
依此类推。
11.删除重复的行
作为以前的单行代码的必然结果,我们可能希望删除重复的行:
awk '= { print }' FS=, OFS=';' file CREDITS;EXPDATE;USER;GROUPS 99;01 jun 2016;sylvain;team:::admin 52;01 dec 2016;sonia;team 52;01 dec 2016;sonia;team 25;01 jan 2019;sonia;team 10;01 jan 2019;sylvain;team:::admin 8;12 jun 2016;öle;team:support 17;05 apr 2019;abhishek;guest
唯一的区别是使用逻辑而不是运算符(!
)来反转表达式的真值。
虚假的变成了事实,虚假的变成了事实。
逻辑上对++
后增量的影响绝对没有影响,后者的工作原理与以前完全相同。
C.字段和记录分隔符魔术
12.更改字段分隔符
awk '(=) || 1 { print }' FS=, OFS=';' file CREDITS;EXPDATE;USER;GROUPS 99;01 jun 2016;sylvain;team:::admin 52;01 dec 2016;sonia;team 52;01 dec 2016;sonia;team 25;01 jan 2019;sonia;team 10;01 jan 2019;sylvain;team:::admin 8;12 jun 2016;öle;team:support 17;05 apr 2019;abhishek;guest
该程序将FS和OFS变量设置为使用逗号作为输入字段分隔符,使用分号作为输出字段分隔符。
因为只要我们没有更改字段,AWK都不会更改输出记录,所以使用$1 = $1技巧来迫使AWK打破记录并使用输出字段分隔符重新组合记录。
请记住,这里的默认操作块是{print}
。
因此,我们可以将其更明确地重写为:
awk '{ =; print }' FS=, OFS=';' file CREDITS;EXPDATE;USER;GROUPS 99;01 jun 2016;sylvain;team:::admin 52;01 dec 2016;sonia;team 52;01 dec 2016;sonia;team 25;01 jan 2019;sonia;team 10;01 jan 2019;sylvain;team:::admin 8;12 jun 2016;öle;team:support 17;05 apr 2019;abhishek;guest
我们可能已经注意到,这两个示例也都删除了空行。
为什么?
好吧,请记住AWK转换规则:一个空字符串是“ false”。
所有其他字符串均为“ true”。
表达式$1 = $1
是一个改变$1
的情感。
但是,这也是一个表达。
并且它的计算结果为$1的值–对于空字符串,该值为“ false”。
如果我们确实想要所有行,则可能需要编写类似以下内容的代码:
awk '=' file CREDITS,EXPDATE,USER,GROUPS 99,01 jun 2016,sylvain,team:::admin 52,01 dec 2016,sonia,team 52,01 dec 2016,sonia,team 25,01 jan 2019,sonia,team 10,01 jan 2019,sylvain,team:::admin 8,12 jun 2016,öle,team:support 17,05 apr 2019,abhishek,guest
我们还记得&&
运算符吗?
这是合乎逻辑的与。
“ ||”是逻辑“或者”。
由于运算符的优先级规则,此处必须加上括号。
没有它们,该模式将被错误地解释为$1 =($1 || 1)。
作为练习,我让我们测试一下结果会有什么不同。
最后,如果我们不太热衷算术,那么我敢打赌,我们会喜欢这种更简单的解决方案:
awk '{ print }' FS=, ORS=' ' file; echo USER sylvain sonia sonia sonia sylvain öle abhishek
13.删除多个空格
awk '/[^[:space:]]/{ print }' FS=, ORS=' ' file; echo USER sylvain sonia sonia sonia sylvain öle abhishek
该程序与上一个程序几乎相同。
但是,我将字段分隔符保留为其默认值。
因此,将多个空格用作输入字段分隔符,但仅将一个空格用作输出字段分隔符。
将多个空格合并为一个空间具有很好的副作用。
14.使用AWK连接线
我们已经使用了OFS,即输出字段分隔符。
我们可能已经猜到了,它具有ORS
副本来指定输出记录分隔符:
awk '/[^[:space:]]/{ print }' FS=, ORS='+' file; echo USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek+
其中我在每条记录之后使用空格代替换行符。
这种单线在某些用例中就足够了,但仍然有一些缺点。
最明显的是,它不会丢弃仅包含空格的行(öle之后的多余空格由此而来)。
因此,我可能最终会使用普通的正则表达式来代替:
awk '/[^[:space:]]/{ print SEP ; SEP="+" }' FS=, ORS='' file; echo USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek
现在比较好,但是仍然可能存在问题。
如果将分隔符更改为可见的内容,将会更加明显:
awk '+ { printf("%s ", ) }' FS=, file; echo sylvain sonia sonia sonia sylvain öle abhishek
该行的末尾有一个另外的分隔符,因为字段分隔符是在每条记录之后写入的。
包括最后一个。
为了解决这个问题,我将从第二个输出记录开始,重写程序以在记录之前显示自定义分隔符。
awk '+ { printf("%s\n", ) }' FS=, file sylvain sonia sonia sonia sylvain öle abhishek
由于我自己负责添加分隔符,因此我还将标准AWK输出记录分隔符设置为空字符串。
但是,当我们开始处理分隔符或者格式设置时,这可能是我们应该考虑使用printf
函数而不是print
语句的标志。
正如我们现在所看到的。
D.字段格式
我已经提到AWK和C编程语言之间的关系。
除其他外,AWK从C语言标准库继承了强大的printf
函数,从而可以很好地控制发送到输出的文本的格式。
printf函数将格式作为第一个参数,既包含将逐字输出的纯文本,又包含用于格式化输出的不同部分的通配符。
通配符由“%”字符标识。
最常见的是“%s”(用于字符串格式化),“%d”(用于整数格式化)和“%f”(用于浮点数格式化)。
因为这可能很抽象,所以让我们看一个例子:
awk '+ { printf("%10s | %4d\n", , ) }' FS=, file sylvain | 99 sonia | 52 sonia | 52 sonia | 25 sylvain | 10 öle | 8 abhishek | 17
我们可能会注意到,与print
语句相反,printf
函数未使用OFS'和
ORS`值。
因此,如果我们想要一些分隔符,则必须像在格式字符串末尾添加一个空格字符一样,明确地提及它。
这是完全控制输出所要付出的代价。
虽然根本不是格式说明符,但这是介绍\ n
表示法的绝佳时机,该表示法可在任何AWK字符串中用于表示换行符。
awk '+ { printf("%-10s | %04d\n", , ) }' FS=, file sylvain | 0099 sonia | 0052 sonia | 0052 sonia | 0025 sylvain | 0010 öle | 0008 abhishek | 0017
15.产生表格结果
AWK强制基于分隔符的记录/字段数据格式。
但是,使用printf
函数,我们还可以生成固定宽度的表格输出。
因为printf
语句中的每个格式说明符都可以接受可选的width参数:
awk '+ { SUM+=; NUM+=1 } END { printf("AVG=%f",SUM/NUM); }' FS=, file AVG=37.571429
如我们所见,通过指定每个字段的宽度,AWK将它们用空格填充到左侧。
对于文本,通常最好在右边填充,这可以使用负宽度数字来实现。
另外,对于整数,我们可能希望将字段填充为零而不是空格。
这可以通过在字段宽度之前使用显式0来获得:
awk '+ { SUM+=; NUM+=1 } END { printf("AVG=%6.1f",SUM/NUM); }' FS=, file AVG= 37.6
16.处理浮点数
%f
格式没有太多解释……
awk ' { print toupper(awk '{ = toupper(substr(,1,1)) substr(,2) } ' FS=, OFS=, file CREDITS,EXPDATE,USER,GROUPS 99,01 jun 2016,Sylvain,team:::admin 52,01 dec 2016,Sonia,team 52,01 dec 2016,Sonia,team 25,01 jan 2019,Sonia,team 10,01 jan 2019,Sylvain,team:::admin 8,12 jun 2016,Öle,team:support 17,05 apr 2019,Abhishek,guest); }' file 99,01 JUN 2016,SYLVAIN,TEAM:::ADMIN 52,01 DEC 2016,SONIA,TEAM 52,01 DEC 2016,SONIA,TEAM 25,01 JAN 2019,SONIA,TEAM 10,01 JAN 2019,SYLVAIN,TEAM:::ADMIN 8,12 JUN 2016,ÖLE,TEAM:SUPPORT 17,05 APR 2019,ABHISHEK,GUEST
…也许除了说我们几乎总是想显式设置显示结果的字段宽度和精度:
awk '+ { split(, DATE, " "); print ,, DATE[2], DATE[3] }' FS=, OFS=, file 99,sylvain,jun,2016 52,sonia,dec,2016 52,sonia,dec,2016 25,sonia,jan,2019 10,sylvain,jan,2019 8,öle,jun,2016 17,abhishek,apr,2019
其中字段宽度为6,这意味着该字段将占据6个字符的空间(包括点,并最终像往常一样在左侧填充空格)。
.1精度表示我们要在点后显示带有1个小数的数字。
我让我们猜测%06.1
将会显示什么。
E.在AWK中使用字符串函数
除了printf
函数外,AWK还包含其他一些不错的字符串操作函数。
在此领域,像Gawk这样的现代实现以较低的可移植性为代价,具有更丰富的内部功能。
就我自己而言,我将仅停留在几个POSIX定义的函数中,该函数应该在任何地方都可以使用。
17.将文本转换为大写
我经常使用它,因为它很好地处理了国际化问题:
awk '+ { split(, GRP, ":"); print , GRP[1], GRP[2] }' FS=, file sylvain team sonia team sonia team sonia team sylvain team öle team support abhishek guest
实际上,这可能是将文本从shell转换为大写的最佳和最可移植的解决方案。
18.更改字符串的一部分
使用“ substr”命令,我们可以按给定的长度分割字符串。
其中我只用它大写第三个字段的第一个字符:
awk '+ { split(, GRP, /:+/); print , GRP[1], GRP[2] }' FS=, file sylvain team admin sonia team sonia team sonia team sylvain team admin öle team support abhishek guest
substr函数使用初始字符串,要提取的第一个字符的(从1开始)索引以及要提取的字符数。
如果缺少最后一个参数,则“ substr”将获取字符串的所有其余字符。
因此,substr($3,1,1)
的计算结果为$3
的第一个字符,而substr($3,2)
的计算结果为其余字符。
19.在子字段中拆分字段
AWK记录字段数据模型非常好。
但是,有时我们希望根据某些内部分隔符将字段本身分为几个部分:
awk '+ { gsub(/+/, "-", ); print }' FS=, file 99 01-jun-2016 sylvain team:::admin 52 01-dec-2016 sonia team 52 01-dec-2016 sonia team 25 01-jan-2019 sonia team 10 01-jan-2019 sylvain team:::admin 8 12-jun-2016 öle team:support 17 05-apr-2019 abhishek guest
出乎意料的是,即使我的某些字段被多个空格隔开,此方法仍然有效。
主要是出于历史原因,当分隔符为单个空格时,“ split”将考虑“元素之间由空白分隔”。
而且不只是一个。
FS特殊变量遵循相同的约定。
但是,通常情况下,一个字符串与一个字符匹配。
因此,如果我们需要更复杂的内容,则必须记住字段分隔符是扩展的正则表达式。
举例来说,让我们看一下如何使用冒号作为分隔符的看起来像是多值字段的group字段:
awk 'BEGIN { printf("UPDATED: "); system("date") } /^UPDATED:/{ next } 1' file UPDATED: Thu Nov 15 00:31:03 CET 2016 CREDITS,EXPDATE,USER,GROUPS 99,01 jun 2016,sylvain,team:::admin 52,01 dec 2016,sonia,team 52,01 dec 2016,sonia,team 25,01 jan 2019,sonia,team 10,01 jan 2019,sylvain,team:::admin 8,12 jun 2016,öle,team:support 17,05 apr 2019,abhishek,guest
我本来希望每个用户最多显示两个组,但对于大多数用户只显示一个。
该问题是由多次出现分隔符引起的。
因此,解决方案是:
awk '+ { CMD | getline ; close(CMD); print }' CMD="uuid -v4" FS=, OFS=, file 99,01 jun 2016,sylvain,team:::admin,5e5a1bb5-8a47-48ee-b373-16dc8975f725 52,01 dec 2016,sonia,team,2b87e9b9-3e75-4888-bdb8-26a9b34facf3 52,01 dec 2016,sonia,team,a5fc22b5-5388-49be-ac7b-78063cbbe652 25,01 jan 2019,sonia,team,3abb0432-65ef-4916-9702-a6095f3fafe4 10,01 jan 2019,sylvain,team:::admin,592e9e80-b86a-4833-9e58-1fe2428aa2a2 8,12 jun 2016,öle,team:support,3290bdef-fd84-4026-a02c-46338afd4243 17,05 apr 2019,abhishek,guest,e213d756-ac7f-4228-818f-1125cba0810f
用斜杠(而不是引号)表示文字是正则表达式,而不是纯字符串,加号表示此表达式将匹配一个或者多个出现的前一个字符。
因此,在那种情况下,每个分隔符都由一个或者几个连续的冒号组成(具有最长的序列)。
20.搜索并替换为AWK命令
说到正则表达式,有时我们希望像seds ///g
命令一样执行替换,但是只在一个字段上执行。
在这种情况下,我们需要gsub
命令:
awk '+ { cmd = sprintf(FMT, ); cmd | getline ; close(cmd); print }' FMT='date -I -d "%s"' FS=, file 99 2016-06-01 sylvain team:::admin 52 2016-12-01 sonia team 52 2016-12-01 sonia team 25 2019-01-01 sonia team 10 2019-01-01 sylvain team:::admin 8 2016-06-12 öle team:support 17 2019-04-05 abhishek guest
gsub函数使用正则表达式进行搜索,替换字符串以及包含要在适当位置修改的文本的变量。
如果后面缺少该字段,则假定$0。
F.在AWK中使用外部命令
AWK的另一个重要功能是我们可以轻松地调用外部命令来处理数据。
基本上有两种方法:使用“系统”指令来调用程序,并使其在AWK输出流中混合其输出。
或者使用管道,以便AWK可以捕获外部程序的输出以更好地控制结果。
它们本身可能是一个巨大的主题,但是这里有一些简单的示例向我们展示这些功能背后的强大功能。
21.在文件顶部添加日期
awk '+ { CMD | getline ; print }' CMD='od -vAn -w4 -t x /dev/urandom' FS=, file 99 01 jun 2016 sylvain team:::admin 1e2a4f52 52 01 dec 2016 sonia team c23d4b65 52 01 dec 2016 sonia team 347489e5 25 01 jan 2019 sonia team ba985e55 10 01 jan 2019 sylvain team:::admin 81e9a01c 8 12 jun 2016 öle team:support 4535ba30 17 05 apr 2019 abhishek guest 80a60ec8
在该AWK程序中,我首先显示UPDATED。
然后程序调用外部的“ date”命令,该命令将在该阶段AWK生成的文本之后立即在输出上发送其结果。
AWK程序的其余部分仅删除最终出现在文件中的一条update语句,并打印所有其他行(使用规则“ 1”)。
注意next
语句。
它用于中止当前记录的处理。
这是一种忽略输入文件中某些记录的标准方法。
22.从外部修改字段
对于更复杂的情况,我们可能需要考虑| AWK的getline VARIABLE
成语:
这将运行存储在CMD
变量中的命令,读取该命令输出的第一行,并将其存储到变量$5中。
请特别注意close语句,在这里至关重要,因为我们希望AWK每次执行CMD |时都创建一个新的外部命令实例。 getline
语句。
如果没有close语句,则AWK会尝试从同一命令实例读取几行输出。
23.调用动态生成的命令
AWK中的命令只是纯字符串,没有任何特殊之处。
管道运算符触发了外部程序的执行。
因此,如果需要,可以使用AWK字符串操作函数和运算符动态构造任意复杂的命令。
我们已经遇到了printf
函数。
sprintf非常相似,但是将返回构建的字符串,而不是将其发送到输出。
24.联接数据
为了向我们展示close语句的目的,我让我们尝试最后一个示例:
##代码##与上面使用uuid
命令的示例相反,在AWK程序运行时,这里只有一个od
实例启动,并且在处理每条记录时,我们又读取了同一进程输出的一行。