AWK:使用内联if-then-else的可靠方法

时间:2018-07-12 07:17:29

标签: awk

在将输入文件的字段与分隔符(逗号,冒号,分号...)连接在一起时,我经常使用内联if-then-else语句(...?...:...)

典型的用例是使用输入文件的字段构建字符串,并将该字符串存储在数组中,如下所示:

$ cat file
a 1
b 2
a 3
c 1
b 26
c 4
c 2

为了转置此类文件,我使用以下awk脚本:

awk '{a[$1]=a[$1](a[$1]?",":"")$2}END{for(i in a) printf "%s:%s\n",i,a[i]}' file

I was told认为,使用内联if-then-else的这种方式还不够健壮,我应该使用这种方式:

awk '{a[$1]=($1 in a?a[$1]",":"")$2}END{for(i in a) printf "%s:%s\n",i,a[i]}' file

显然,对于这个简单的输入文件,两个脚本的结果是相同的。

那么为什么第二个脚本会更“健壮”?

我实际上是在寻找一个输入示例,该示例会使第一个脚本失败,但不会使另一个脚本失败。

2 个答案:

答案 0 :(得分:2)

首先,我猜您在第一个awk单行代码中又添加了一个a[$1]=。应该是:

awk '{a[$1]=a[$1](a[$1]?",":"")$2}....

我认为您的问题是关于为什么检查$1 in a比检查a[$1]更好。两种单线均可为您提供输入。因为字符串串联使a[$1]成为字符串。对于字符串,如果不为空,则awk会将其评估为true。也适用于字符串"0"

但是,如果您必须以某种方式将输入作为数字处理以适合所需的输出,则两次布尔检查可能会不同。如果数字为0,则awk的值将为false

看看这个例子:

kent$  awk 'BEGIN{if(0)print "number"; if("0")print "string"}'
string

您看到只打印了后一个。

因此$1 in a更好,因为无论a[$1]存储数字还是字符串,它都将始终有效。

但是,如果您在代码中设置了if(a[$1]),则可以使用a[$1]检查,并且100%确保boolean a[$1]会提供您期望的结果。例如,您在代码中进行了字符串串联""$2。或者您设置类似a[$1]=1

通常$1 in a更好。

答案 1 :(得分:1)

除了@Kent提到的内容外,还要考虑一般字段可以为空字符串。提示您当前问题的original question I commented on输入了CSV,因此,我们现在再次使用该输入是为了获得比空格分隔更好的可见性:

$ cat file
a,1
b,
a,3
c,
b,26
c,4
c,2

$ awk -F',' '{a[$1]=a[$1](a[$1]?",":"")$2}END{for(i in a) printf "%s:%s\n",i,a[i]}' file
a:1,3
b:26
c:4,2

$ awk -F',' '{a[$1]=($1 in a?a[$1]",":"")$2}END{for(i in a) printf "%s:%s\n",i,a[i]}' file
a:1,3
b:,26
c:,4,2