我最近在网站上找到了一个脚本:
这是相对陈旧的,需要50个代表评论,我没有.我试图让它工作,并且不太了解awk语法,但我正在努力.在我正在使用的测试文件中:
-3.793 0.9804E+00 0.3000E+02
-3.560 0.1924E-01 0.3000E+02
-3.327 0.3051E-04 0.3000E+02
-3.093 0.3567E-08 0.3000E+02
-2.860 0.3765E-06 0.3000E+02
-2.627 0.1119E-02 0.3000E+02
-2.394 0.2520E+00 0.3006E+02
Run Code Online (Sandbox Code Playgroud)
这是脚本:
{
if ($fld > tgt) {
del = $fld - tgt
if ( (del < minGtDel) || (++gtHit == 1) ) {
minGtDel = del
minGtVal = $fld
}
}
else if ($fld < tgt) {
del = tgt - $fld
if ( (del < minLtDel) || (++ltHit == 1) ) {
minLtDel = del
minLtVal = $fld
}
}
else {
minEqVal = $fld
}
}
END {
print (minGtVal == "" ? "NaN" : minGtVal)
print (minLtVal == "" ? "NaN" : minLtVal)
}
Run Code Online (Sandbox Code Playgroud)
当这样运行时:
$ awk -v fld=1 -v tgt=-3 -f awk DOSCAR
Run Code Online (Sandbox Code Playgroud)
生产:
-2.860
NaN
Run Code Online (Sandbox Code Playgroud)
即使有一个下限,我也不太确定如何解决它.原帖没有负数,所以他们没有这个问题.任何帮助表示赞赏.
输入文件中有一个空行,触发了经典的awk陷阱.
核心问题是awk比较运算符的奇怪行为,它不需要您指定是否需要数字或字符串比较.(<意见>这正是为什么自动比较算子是一个坏主意.</ opinion>)
简而言之,awk中有三种标量类型:数字,字符串和"数字字符串".程序中的文字是数字或字符串,算术运算符的结果总是一个数字,而字符串连接的结果总是一个字符串.但是您所比较的值 - $fld和tgt- 都可能是"数字字符串",因为它们来自用户输入.
"数字字符串"是来自用户输入的字符串,恰好"看起来像"一个数字.总的来说,"看起来像一个数字"的定义并不令人惊讶,除了一个细节:空字符串不计算.
如果比较两个数字,则比较是数字.如果比较两个字符串,则比较是字典.但是,如果您要比较的东西中的一个(或两个)可能是"数字字符串",那么比较的类型取决于它是否实际上是"数字字符串".如果它是"数字字符串",它会变成一个数字; 否则,另一个值变成一个字符串.
因此,如果$fld是一个空字符串,那么将它与它进行比较tgt将是字符串比较而不是数字比较.空字符串是字符串比较的最小可能字符串,因此它会变小.但是,当你再计算时$fld - tgt,$fld则会强制转换为数字,在这种情况下,空字符串变为0.
所以有两种可能性.最简单的是强制$fld改为数字; 这至少是一致的:
{
val = $fld + 0
if (val > tgt) {
del = val - tgt
if ( (del < minGtDel) || (++gtHit == 1) ) {
minGtDel = del
minGtVal = val
}
}
else if (val < tgt) {
del = tgt - val
if ( (del < minLtDel) || (++ltHit == 1) ) {
minLtDel = del
minLtVal = val
}
}
else {
minEqVal = val
}
}
END {
print (minGtVal == "" ? "NaN" : minGtVal)
print (minLtVal == "" ? "NaN" : minLtVal)
}
Run Code Online (Sandbox Code Playgroud)
另一种方法是消除指示字段不能是数字的行.对数值进行简单且通常可靠的测试是将值与自身进行比较,并将其作为一个数字进行比较:
(val = $fld + 0) == $fld {
if (val > tgt) {
del = val - tgt
if ( (del < minGtDel) || (++gtHit == 1) ) {
minGtDel = del
minGtVal = val
}
}
else if (val < tgt) {
del = tgt - val
if ( (del < minLtDel) || (++ltHit == 1) ) {
minLtDel = del
minLtVal = val
}
}
else {
minEqVal = val
}
}
END {
print (minGtVal == "" ? "NaN" : minGtVal)
print (minLtVal == "" ? "NaN" : minLtVal)
}
Run Code Online (Sandbox Code Playgroud)