4

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

有解决方法吗?

4

3 回答 3

5

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

请参阅POSIX-1.2004 awk 规范词汇约定

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 (大概你正在使用nawkor mawk)表现得“正确”。gawk(从 3.1 版开始)默认支持非十进制(八进制和十六进制)文字数字,尽管使用--posix开关将其关闭,如预期的那样。

在这种情况下,正常的解决方法是使用定义的数字字符串行为,其中数字字符串将被有效地解析为支持-prefixed 数字的 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 还指出

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 版awk)允许strtod()和实现相关的行为,不需要数字符合词汇约定。INF这也应该(隐式)支持NANLexical 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 问题的公认答案具有可移植性解决方法。

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

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

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

于 2013-10-23T12:51:45.153 回答
1

您是否坚持使用旧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
于 2013-10-22T06:42:13.217 回答
1

无论您使用的是什么 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
于 2013-10-22T22:25:22.310 回答