awk模式中的十六进制文字

时间:2013-10-22 06:04:35

标签: macos unix awk

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

有解决方法吗?

3 个答案:

答案 0 :(得分:5)

你在这里处理两个相似但不同的问题,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()和实现相关的行为不需要数字符合词汇约定。这应该(隐含地)支持INFNAN。类似地修改了词汇约定中的文本,以选择性地允许带有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的移植版here

答案 1 :(得分: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

答案 2 :(得分: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