在AWK中,为什么像$(NF + 1)这样不存在的字段不等于零?

Ben*_*oyt 13 awk

当使用AWK时,我很难理解为什么不存在的字段(后面的字段$NF)不等于等于数字零.

在下面的示例中,输入行有两个字段,因此根据规范$3应该是"未初始化的值"并且比较等于0.换句话说,$3 == 0应该返回true,但正如您在下面看到的那样它返回false:

$ echo '1 2' | awk '{ print($3 == 0 ? "t" : "f") }'
f
Run Code Online (Sandbox Code Playgroud)

"One True AWK"(版本20121220)和GNU AWK(版本4.2.1)的行为方式相同.这是GNU AWK输出:

$ echo '1 2' | gawk '{ print($3 == 0 ? "t" : "f") }'
f
Run Code Online (Sandbox Code Playgroud)

根据POSIX AWK规范,不存在的字段$3应该是未初始化的值:

对不存在的字段(即$ NF之后的字段)的引用应评估为未初始化的值.

此外,==如果一个操作数是数字而另一个是未初始化的值,则应该以数字方式进行比较:

比较(使用'<',"<=","!=","==",">"和"> ="运算符)如果两个操作数都是数字,则应以数字方式进行比较,如果一个是数字,则other的字符串值是数字字符串,或者如果一个是数字而另一个是未初始化的值.否则,操作数应根据需要转换为字符串......

最后,未初始化的值的"数值"应为零:

未初始化的值应具有零值的数值和空字符串的字符串值.

将此与未初始化的变量进行对比,该变量的确比较等于零:

$ awk 'BEGIN { print(x == 0 ? "t" : "f") }'
t
Run Code Online (Sandbox Code Playgroud)

所以在我们的第一个例子中,$3应该是一个未初始化的值,==应该用数字比较它,它的数值应该是零.因此在我看来$3 == 0 ? "t" : "f"应该输出t而不是f.

任何人都可以帮助我理解为什么它没有,或者帮助我看看我是如何误读规范的?

kva*_*our 6

有一个有趣的通道awk编程语言中阿尔弗雷德V.阿霍,布赖恩W. Kernighan和彼得·温伯格(1988)(在这里预订):

使用数值0和字符串值创建未初始化的变量"".不存在的字段和显式为null的字段只有字符串值""; 它们不是数字,但是当强制转换为数字时,它们会获得数值0.

来源:AWK编程语言,第2.2节,第45页

此外:

未初始化的变量具有数值0和字符串值"".因此,如果x是未初始化的,

if (x) ...
Run Code Online (Sandbox Code Playgroud)

是假的,而且

if (!x) ...
if (x == 0) ...
if (x == "") ...
Run Code Online (Sandbox Code Playgroud)

都是真的.但请注意

if (x == "0") ...
Run Code Online (Sandbox Code Playgroud)

是假的.

字段的类型在可能的情况下由上下文确定; 例如,$1++ 意味着,$1如果有必要,必须强制转换为数字,并 $1 = $1 "," $2 暗示$1,并$2在必要时将被强制转换为字符串.

在无法可靠地确定类型的情况下,例如,

if {$1 == $2) ...
Run Code Online (Sandbox Code Playgroud)

每个字段的类型根据输入确定.所有字段都是字符串 ; 此外,仅包含数字的每个字段被视为数字.显式为null的字段具有字符串值""; 它们不是数字.不存在的字段(即字段过去NF)和$0空行也是这样处理的.

因为它是字段,所以它是由创建的数组元素split.

来源:AWK编程语言,附录A,初始化,比较和类型强制,第192页

在我看来,这些行很好地解释了观察到的行为,似乎大多数程序也遵循这一点.


最重要的是,在rici职位的附录中:

在调查GNU Awk 4.2.1的源代码时,我发现:

  • 未初始化的变量被赋值NodeNnull_string具有标志的named :

    main.c: Nnull_string->flags = (MALLOC|STRCUR|STRING|NUMCUR|NUMBER);
    
    Run Code Online (Sandbox Code Playgroud)
  • 不存在的字段被分配给名为的节点Null_field,该节点被重新定义Nnull_string为:

    field.c: *Null_field = *Nnull_string;
    field.c: Null_field->valref = 1;
    field.c: Null_field->flags = (STRCUR|STRING|NULL_FIELD); /* do not set MALLOC */
    
    Run Code Online (Sandbox Code Playgroud)

字段具有值(from awk.h):

#       define  STRING  0x0002       /* assigned as string */
#       define  STRCUR  0x0004       /* string value is current */
#       define  NUMCUR  0x0008       /* numeric value is current */
#       define  NUMBER  0x0010       /* assigned as number */
#       define  NULL_FIELD 0x2000    /* this is the null field */
Run Code Online (Sandbox Code Playgroud)

比较功能int cmp_nodes(NODE *t1, NODE *t2, bool use_strcmp)中所定义eval.c,只是检查,如果NUMBER标志被设置两者t1t2:

if ((t1->flags & NUMBER) != 0 && (t2->flags & NUMBER) != 0)
    return cmp_numbers(t1, t2);
Run Code Online (Sandbox Code Playgroud)

由于Null_field没有数字字段,它只是假设它代表一个字符串.这一切似乎都与本书引用的内容一致!

此外,来自awk.h:

* STRING and NUMBER are mutually exclusive, except for the special
* case of an uninitialized value, represented internally by
* Nnull_string. They represent the type of a value as assigned.
* Nnull_string has both STRING and NUMBER attributes, but all other
* scalar values should have precisely one of these bits set.
*
* STRCUR and NUMCUR are not mutually exclusive. They represent that
* the particular type of value is up to date.  For example,
*
*   a = 5       # NUMBER | NUMCUR
*   b = a ""    # Adds STRCUR to a, since a string value
*               # is now available. But the type hasn't changed!
*
*   a = "42"    # STRING | STRCUR
*   b = a + 0   # Adds NUMCUR to a, since numeric value
*               # is now available. But the type hasn't changed!
Run Code Online (Sandbox Code Playgroud)