我有一个程序对文件中的列进行求和:
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”或“未分配”。
答案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..."
}