【问题标题】:hexadecimal literals in awk patternsawk 模式中的十六进制文字
【发布时间】:2013-10-30 20:45:54
【问题描述】:

awk 能够将字段解析为十六进制数:

$ echo "0x14" | awk '{print $1+1}'
21 <-- correct, since 0x14 == 20

但是,它似乎无法处理带有十六进制文字的操作:

$ echo "0x14" | awk '$1+1<=21 {print $1+1}' | wc -l
1 <-- correct
$ echo "0x14" | awk '$1+1<=0x15 {print $1+1}' | wc -l
0 <-- incorrect.  awk is not properly handling the 0x15 here

有解决办法吗?

【问题讨论】:

  • @RobertoNavarro 这是一个完全不同的问题。我问的是直接在 awk 命令中使用十六进制文字(如第三个 awk 命令所示)
  • 我一直在尝试不同的变化,如果你愿意,你可以嘲笑这个,但我想我会在我睡觉之前发布它:bash$ hex=20;回声 $((16#$hex+1)) | echo '0x'awk '{printf "%x\n", $1;}'
  • @RobertoNavarro 我很惊讶你没有建议更简单的awk '$1+1&lt;='$((0x15))'{print $1+1}',这实际上非常聪明(如果你回答我会给你一个赞成票)

标签: macos unix awk


【解决方案1】:

您在这里处理两个相似但不同的问题,awk 输入中的非十进制数据和awk 程序中的非十进制文字。

参见the POSIX-1.2004 awk specification词汇约定

8. The token NUMBER shall represent a numeric constant. Its form and numeric value [...]
   with the following exceptions:
    a. An integer constant cannot begin with 0x or include the hexadecimal digits 'a', [...]

所以 awk(可能您使用的是 nawkmawk)表现“正确”。 gawk(从 3.1 版开始)默认支持非十进制(八进制和十六进制)文字数字,但使用 --posix 开关会按预期将其关闭。

在这种情况下,正常的解决方法是使用定义的 数字字符串 行为,其中数字字符串将被有效地解析为 C 标准 atof()strtod() 函数,它支持0x-前缀数字:

$ echo "0x14" | nawk '$1+1<=0x15 {print $1+1}'
<no output>
$ echo "0x14" | nawk '$1+1<=("0x15"+0) {print $1+1}'
21

这里的问题是这不太正确,就像POSIX-1.2004 also states

A string value shall be considered a numeric string if it comes from one of the following: 
   1. Field variables
   ...
and after all the following conversions have been applied, the resulting string would 
lexically be recognized as a NUMBER token as described by the lexical conventions in Grammar

更新:gawk 的目标是“2008 POSIX.1003.1”,但请注意,自 2008 年版(参见 IEEE Std 1003.1 2013 edition awk here)以来,允许 strtod() 和实现相关的行为,不需要数字符合词法公约。这也应该(隐式)支持INFNANLexical Conventions 中的文本进行了类似的修改,以允许使用带有 0x 前缀的十六进制常量。

这不会像gawk 中所希望的那样(考虑到对数字的词法约束):

$ echo "0x14" | gawk  '$1+1<=0x15 {print $1+1}'
1

(注意“错误”的数字答案,它会被 |wc -l 隐藏) 除非你也使用--non-decimal-data

$ echo "0x14" | gawk --non-decimal-data '$1+1<=0x15 {print $1+1}'
21

另见:

SE question 的已接受答案具有可移植性解决方法。

对非十进制数提供两种类型的支持的选项是:

  • 仅使用 gawk,不使用 --posix 和使用 --non-numeric-data
  • 实现一个包装函数来执行十六进制转十进制,并将其与您的文字和输入数据一起使用

如果您搜索“awk dec2hex”,您可以找到许多后者的实例,一个可以通过的实例在这里:http://www.tek-tips.com/viewthread.cfm?qid=1352504。如果你想要像 gawk 的 strtonum() 这样的东西,你可以得到一个可移植的 awk-only 版本 here

【讨论】:

  • 内容丰富!原来("0x15"+0) 在这里工作。它是否也适用于 gawk(没有 -n)
  • 没有任何选项,否:对于转换,它被视为“0”,后跟非十进制垃圾,因此数字为 0。如果您使用--posix,那么它可以工作,这有点奇怪。更奇怪的是-n 也使它工作。这可能是 gawk/awk/POSIX 的尘土飞扬的角落之一。 (对于奖励积分:gawk-3.0.4 中有一个错误导致除数全部错误,当将“0x15”转换为十进制时,它会导致 1.3125(即 21/16)...)
【解决方案2】:

您使用的任何 awk 似乎都已损坏,或者至少不是 POSIX:

$ echo '0x14' | /usr/xpg4/bin/awk '{print $1+1}'
1
$ echo '0x14' | nawk '{print $1+1}'
1
$ echo '0x14' | gawk '{print $1+1}'
1
$ echo '0x14' | gawk --posix '{print $1+1}'
1

获取 GNU awk 并在任何可能有十六进制数的地方使用 strtonum():

$ echo '0x14' | gawk '{print strtonum($1)+1}'
21
$ echo '0x14' | gawk 'strtonum($1)+1<=21{print strtonum($1)+1}'              
21
$ echo '0x14' | gawk 'strtonum($1)+1<=strtonum(0x15){print strtonum($1)+1}'
21

【讨论】:

  • 我认为你没有读过这个问题。 $ echo "0x14" | awk '{print $1+1}' 工作得很好。在模式中使用十六进制文字时出现问题(如在第三种情况下 - 如果十六进制文字工作正常,则 $ echo "0x14" | awk '$1+1&lt;=0x15 {print $1+1}' | wc -l 将是 1,而不是 0)
  • 我确实阅读了这个问题,关键是 echo "0x14" | awk '{print $1+1}' 不能与您的 awk“工作”。它产生您期望/想要的输出,但这不是正确的输出。所有其他 awk,包括我测试的 2 个不同的 POSIX awk,都产生相同的输出,并且它们在处理自身内部的十六进制数字字符串时保持一致(它是字符串,而不是数字)。产生一个人认为它应该产生的输出并不意味着它可以工作,尤其是当它只在某些情况下表现得像你预期的那样时。
  • @SheetJS 这是因为最初的问题的措辞也有点令人困惑......(至少在 IMO)我也必须阅读它两次才能弄清楚 OP 实际上是关于什么的。我什至在本地破解了这里的行,以了解哪些输出是预期的,哪些不是。根据我的定义,“清晰”是另外一回事;)
【解决方案3】:

您还在使用旧的awk 版本吗?我不知道用它来做数学运算的方法(你将不得不等待更好的答案:-)。我可以选择Gawk

-n, --non-decimal-data:识别输入数据中的八进制和十六进制值。请谨慎使用此选项!

所以,要么

echo "0x14" | awk -n '$1+1<=21 {print $1+1}'

echo "0x14" | awk -n '$1+1<=0x15 {print $1+1}'

返回

21

【讨论】:

  • 我没有使用gnu awk,不幸的是-n选项不可用,但让我困惑的是我的版本清楚地解析了输入数据中的十六进制文字(如第一个命令所示echo "0x14" | awk '{print $1+1}').
  • @Nirk:但是该命令不适用于我的版本(GNU Awk 4.1.0)。它返回1。我只知道gawk 的解决方法。对不起。
  • 现在真的很有趣。我希望 gnu 版本能做“正确的事情”。尽管如此,我还是从中学到了一些东西,所以请点赞:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-04
  • 2012-08-15
  • 2016-10-16
  • 1970-01-01
  • 2011-12-09
  • 1970-01-01
相关资源
最近更新 更多