给定一个单列数字文件,调用它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
),而是通过使用实际的数据。这样,总能保证提供有意义的结果。
在您的情况下,您可以通过添加规则使用遇到的第一个值(即第一行中的条目)分别初始化max
和min
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
+inf
和inf
、 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"
可能会变成 或inf
,0
具体取决于实现(在 nawk 和 gawk 中为 0)。
当不加引号时,-inf
和都+inf
变成0
(除了 bawk 中+∞
被理解为空字符串 "" 并转换为0
)。
奇怪的是,当不加引号时,所有内容都inf
被解释为空字符串。
但全部不加引号-inf
,当用作 时变为+inf
0 。inf
var+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
- 如果没有
--posix
,gawk
则会专门解释四个字符串值 '+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