使 awk 对非数字产生错误

使 awk 对非数字产生错误

我有一个程序对文件中的列进行求和:

awk -v col=2 '{sum+=$col}END{print sum}' input-file

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

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

这是一个输入示例:

bob 1
dave 2
alice 3.5
foo bar

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

答案1

一种合理的测试方法是使用类似于以下的测试来比较现场strtod, 哪一个是awk 使用的方法将字符串转换为数字:

$2 !~ / *[+-]?[[:digit:]]/ { print "NAN: " $2; exit 1; }

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

$2 !~ /[+-]?[[:digit:]]/ { print "NAN: " $2; exit 1; }

进一步完善,感谢 Stéphane 的评论和在这里回答:

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

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

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

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

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

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

答案2

我最终得到了这个:

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

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

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

答案3

awk -v col=2 '
    $col+0==0 && $col!~/^[+-]?0/ { print "bad number " $col > "/dev/stderr" } 
    {sum+=$col}
    END{print sum}' input-file

.0如果您希望它也处理或作为;.0e+33的有效表示,您可以将其复杂化。0请注意,awk在将字符串转换为数字时,将忽略尾随垃圾("1.4e1e3"+0,"1.4e1.e7"+0"14+13"+0将全部等于 14)。

答案4

我使用的一个小技巧是尝试从字符串中获取平方根。awk将尽最大努力使其正确,因此如果尾随 0 不是数字,则此方法不适合您。

if ( sqrt(_varname) ) {
 print "this " _varname " is a (positive) number"
} else {
 print "this " _varname " is not a number..."
}

相关内容