awk 中的“inf”不像“-inf”那样工作

awk 中的“inf”不像“-inf”那样工作

给定一个单列数字文件,调用它f,以下 Awk 代码将返回最大值

cat f | awk    '   BEGIN {max = -inf}
                       {if ($1>max) max=$1} 
                   END { print max }
               '

获得最小值的相同方法不会产生任何结果

cat f | awk '
               BEGIN  {min = inf}
                  {if ($1<min) min=$1} 
               END {print min}
            '

但是,如果inf我不使用 ,而是从 开始min = [some large number],如果数字足够大(取决于文件中的内容),则修改后的代码将起作用。

为什么不起作用inf,有什么方法可以让案件min像案件一样工作max,而不必知道文件中的内容?

答案1

实际的任务最好的解决方法是初始化您的最大/最小值,而不是通过虚构的“最小”或“最大”数字(在本例中,这可能无法在您正在使用的框架中实现awk),而是通过使用实际的数据。这样,总能保证提供有意义的结果。

在您的情况下,您可以通过添加规则使用遇到的第一个值(即第一行中的条目)分别初始化maxmin

NR==1{min=$1}

到你的awk脚本。然后,如果第一个值已经是最小值,则后续测试不会覆盖它,最终会产生正确的结果。这同样适用于最大值的搜索,因此在组合搜索中,您可以声明

NR==1{max=min=$1}

inf至于你的方法不起作用的原因awk似乎-inf,@steeldriver 在对您的问题的评论中提供了很好的解释,为了完整起见,我还将对其进行总结:

  • 在 中awk,变量是“动态类型的”,即一切都可以是字符串或数字,具体取决于使用(但awk会“记住”它上次使用的内容并保留该信息以供下一个操作使用)。
  • 每当在代码中发现涉及变量的算术运算时,awk都会尝试将该变量的内容解释为数字并执行该运算,如果成功,则从该处将变量键入为数字。
  • 任何尚未分配任何内容的变量的默认值为空字符串,在算术运算中将其解释为 0。
  • 变量名称(*) inf在 中没有特殊含义awk,因此当这样使用时,它是一个空变量,在算术表达式(例如 )中计算结果为 0 -inf。因此,如果您的数据全部为正,则max变量初始化为的“最大搜索”将起作用,因为它只是 0(因此是最小的非负数)。-inf-inf
  • 然而,在“最小搜索”问题中,初始化min会将inf变量初始化为空字符串,因为不存在可以保证将该空字符串自动转换为数字的算术运算。
  • 所以在后面的比较中

    if ($1<min) min=$1
    

    输入 ,$1与字符串值进行比较,这就是为什么也awk将其视为$1字符串,并执行词典编纂的比较而不是数字。

  • 然而,从字典顺序来看,没有什么比空字符串“小”,所以min 绝不被分配一个新值。因此,在本END节中,声明

    print min
    

    打印(仍然)空字符串。

(*) 看斯蒂芬·基特的回答如何一个带有内容的字符串"inf"实际上可以有意义awk

答案2

您的方法不起作用,因为inf在 GNU AWK 的默认非 POSIX 模式下没有特殊含义。结果,它被解释为变量名,并且由于该变量尚未设置为任何内容,因此其值在算术上下文中为 0,在字符串上下文中为空字符串。因此,您的代码只会找到正数的最大值(因为是max在算术上下文中初始化的),而不会找到最小值(因为是min在字符串上下文中初始化的);看管理员蜜蜂的回答了解详情。

要确定文件(或流)中的最小值和/或最大值,您应该遵循中给​​出的建议管理员蜜蜂的回答

但是,如果您使用的是 GNU AWK,您可以计算log(0)以正无穷大或负无穷大初始化变量,并以类似于您的方法的方式使用它:

BEGIN { max = log(0) }
$1 > max { max = $1 }
END { print max }
BEGIN { min = -log(0) }
$1 < min { min = $1 }
END { print min}

与从第一行初始化值相比,这种方法的唯一优点是,当没有处理任何值时,它可以提供独特的结果 - 正无穷大或负无穷大最终成为没有看到任何值的可靠指标。 (还有其他方法可以确定这一点,包括在从第一行初始化时检查空字符串而不是 0。)

使用 POSIX 模式下的 GNU AWK ( POSIXLY_CORRECT=1),或其他 POSIX 兼容的 AWK 解释器,例如,mawk提供"inf"细绳在算术上下文中产生无穷大,这要归功于strtod

BEGIN { max = "-inf" + 0 }
$1 > max { max = $1 }
END { print max }
BEGIN { min = "+inf" + 0 }
$1 < min { min = $1 }
END { print min}

答案3

事实上,无穷大有三个值:-inf +infinf、 and ,为了给本来很简单的问题增加更多的复杂性,在 awk 中,有带引号和不带引号的代码常量。

为了说明我的意思,请尝试这个(awk 4.2.1(当前 Debian 10)中的 shell 代码):

for cmd in original-awk "busybox awk" mawk nawk awk; do
    printf '%-6.5s' "$cmd"
    $cmd 'BEGIN {
        a="-inf";b="+inf";c="inf";
        d= -inf ;e= +inf; f= inf;
        printf "-∞%4s%4s +∞%4s%4s ∞%4s%4s | -∞%4s%4s +∞%4s%4s ∞%4s%4s\n",a,a+0,b,b+0,c,c+0,d,d+0,e,e+0,f,f+0}
    ' file

要得到:

bawk  -∞-inf-inf +∞+inf inf ∞ inf inf | -∞   0   0 +∞       0 ∞       0
busyb -∞-inf-inf +∞+inf inf ∞ inf inf | -∞   0   0 +∞   0   0 ∞       0
mawk  -∞-inf-inf +∞+inf inf ∞ inf inf | -∞   0   0 +∞   0   0 ∞       0
nawk  -∞-inf-inf +∞+inf inf ∞ inf   0 | -∞   0   0 +∞   0   0 ∞       0
gawk  -∞-inf-inf +∞+inf inf ∞ inf   0 | -∞   0   0 +∞   0   0 ∞       0

该表显示了带引号和不带引号的变量赋值 (abcdef)。
对于每种情况,由 awk 读取并转换为数字 (var+0) 的值。

这就是说,"-inf"即使是数字,a 也会保持原样,a"+inf"会转换为数字inf(不带符号),并且引用的 a"inf"可能会变成 或inf0具体取决于实现(在 nawk 和 gawk 中为 0)。

当不加引号时,-inf和都+inf变成0(除了 bawk 中+∞被理解为空字符串 "" 并转换为0)。

奇怪的是,当不加引号时,所有内容都inf被解释为空字符串。

但全部不加引号-inf,当用作 时变为+inf0 。infvar+0

因此,对于您想做的事情,您需要引用"-inf"并且"+inf"永远不要inf

cat file | awk  '  BEGIN { max = "-inf"+0; min = "+inf"+0 }
                         { if ($1>max) max=$1
                           if ($1<min) min=$1
                         } 
                   END   { print min, max }
                '

也许,理解它的一种更简单(不可移植的方式)是执行:

gawk 'BEGIN{
               a="-inf";b="+inf";c="inf";
               d= -inf ;e= +inf; f= inf;

               print a,   typeof(a),   b,   typeof(b),   c,   typeof(c)
               print a+0, typeof(a+0), b+0, typeof(b+0), c+0, typeof(c+0)

               print d,typeof(d),e,typeof(e),f,typeof(f)
               print d+0,typeof(d+0),e+0,typeof(e+0),f+0,typeof(f+0)
      }'

将打印:

-inf string +inf string inf string
-inf number inf number 0 number
0 number 0 number  unassigned
0 number 0 number 0 number

当然,正确且可移植的解决方案是从一开始就为max和变量赋值:min

cat file | awk  '  NR==1 { min = max = $1 }
                         { if ($1>max) max=$1
                           if ($1<min) min=$1
                         } 
                   END   { print min, max }
                '

---

说明来自 awk 手册是:

  • 使用--posix命令行选项,gawk就可以“放手”。字符串值直接传递给系统库的 strtod() 函数,如果它成功返回一个数值,则使用该数值。根据定义,结果不可跨不同系统移植。他们也有点令人惊讶:
$ echo influence | gawk --posix '{ print $1 + 0 }'
  -| inf
$ echo 0xDeadBeef | gawk --posix '{ print $1 + 0 }'
  -| 3735928559
  • 如果没有--posixgawk则会专门解释四个字符串值 '+inf'、'-inf'、'+nan' 和 '-nan',生成相应的特殊数值。前导符号充当一个信号,让用户(和用户)感到该值确实是数字。不支持十六进制浮点(除非您还使用--non-decimal-data,但不建议这样做)。例如:
$ echo nanny | gawk '{ print $1 + 0 }'
  -| 0
$ echo +nan | gawk '{ print $1 + 0 }'
  -| +nan
$ echo 0xDeadBeef | gawk '{ print $1 + 0 }'
  -| 0

gawk忽略四个特殊值的大小写。因此,“+nan”和“+NaN”是相同的。

除了处理输入之外,gawk当值为 NaN 或无穷大时,还需要在输出上打印“正确”值。从版本 4.2.2 开始,对于此类值,gawk将打印刚刚描述的四个字符串之一:“+inf”、“-inf”、“+nan”或“-nan”。类似地,在 POSIX 模式下,使用值的格式字符串(无论是什么)gawk打印系统 C 函数的结果。printf()%g

相关内容