使 awk 在非数字上产生错误

Nic*_*ell 4 awk numeric-data

我有一个程序可以对文件中的一列求和:

awk -v col=2 '{sum+=$col}END{print sum}' input-file
Run Code Online (Sandbox Code Playgroud)

但是,它有一个问题:如果给它一个没有数字数据的文件,(或者如果缺少一个数字),它会将其解释为零。

如果其中一个字段无法解析为数字,我希望它产生错误。

这是一个示例输入:

bob 1
dave 2
alice 3.5
foo bar
Run Code Online (Sandbox Code Playgroud)

我希望它产生错误,因为 'bar' 不是数字,而不是忽略错误。

Jef*_*ler 7

一种合理的测试方法是使用类似于 的测试来比较字段strtod,这是awk 用于将字符串转换为数字的方法:

$2 !~ / *[+-]?[[:digit:]]/ { print "NAN: " $2; exit 1; }
Run Code Online (Sandbox Code Playgroud)

以上与 strtod 的不同之处在于它不将 INFINITY 或 NAN 视为“数字”。在 awk 的默认字段拆分行为下,前导空间要求可以放宽——这意味着字段永远不会包含前导空间:

$2 !~ /[+-]?[[:digit:]]/ { print "NAN: " $2; exit 1; }
Run Code Online (Sandbox Code Playgroud)

进一步改进,感谢 Stéphane 的评论和回答

$2 !~ /^[+-]?([[:digit:]]*\.?[[:digit:]]*([eE][-+]?[[:digit:]]+)?|0[xX][[:xdigit:]]*\.?[[:xdigit:]]*([pP][-+]?[[:digit:]]+)?)$/ { print "NAN: " $2; exit 1; }
Run Code Online (Sandbox Code Playgroud)

为了稍微好一点的易读性,该正则表达式是:

/^[+-]?([[:digit:]]*\.?[[:digit:]]*([eE][-+]?[[:digit:]]+)?|\
        0[xX][[:xdigit:]]*\.?[[:xdigit:]]*([pP][-+]?[[:digit:]]+)?)$/
Run Code Online (Sandbox Code Playgroud)

... 其目的是允许可能的前导 + 或 -,然后是浮点数或十六进制数。浮点数具有可选的前导数字、选项分隔符(此处固定为句点.),后跟一定数量的数字,可选后跟指数。十六进制数字必须以0x或开头0X,后跟十六进制数字、分隔符、更多十六进制数字,并可选择后跟“幂”(指数)。整个第二个字段必须匹配这些格式之一(由^和锚定$)。出于这个问题的目的,这里省略了 NAN 和 INFINITY 选项。

另一种选择是强制进行数字转换,然后将其与零进行比较,然后进一步将原始输入与将转换为零的内容进行比较;更具体地说,它是否以可选的 + 或 - 开头,然后是后跟零,还是后跟一个句点和零:

{ number=0 + $2;
  if (!number && $2 !~ /^[+-]?(0+)|\.0+/)
    print "NAN: "$2;
}
Run Code Online (Sandbox Code Playgroud)


Nic*_*ell 5

我结束了这个:

awk -v col=$col '
typeof($col) != "strnum" {
    print "Error on line " NR ": " $col " is not numeric"
    noprint=1
    exit 1
}
{
    sum+=$col
}
END {
    if(!noprint)
        print sum
}' $file
Run Code Online (Sandbox Code Playgroud)

这使用了 typeof,它是一个 GNU awk 扩展。typeof($col)如果$col是有效数字,则返回 'strnum',如果不是,则返回 ' string' 或 'unassigned'。

请参阅 我可以确定 awk 变量的类型吗?