在awk或sed中将十六进制转换为十进制

时间:2011-01-06 12:13:26

标签: sed awk decimal hex

我有一个以逗号分隔的数字列表:

123711184642,02,3583090366663629,639f02012437d4
123715942138,01,3538710295145500,639f02afd6c643
123711616258,02,3548370476972758,639f0200485732

我需要将第3列拆分为3,如下所示:

123711184642,02,3583090366663629,639f02,0124,37d4
123715942138,01,3538710295145500,639f02,afd6,c643
123711616258,02,3548370476972758,639f02,0048,5732

并将最后两列中的数字转换为十进制:

123711184642,02,3583090366663629,639f02,292,14292
123715942138,01,3538710295145500,639f02,45014,50755
123711616258,02,3548370476972758,639f02,72,22322

7 个答案:

答案 0 :(得分:22)

答案 1 :(得分:9)

这似乎有效:

awk -F, '{ p1 =       substr($4,  1, 6);
           p2 = ("0x" substr($4,  7, 4)) + 0;
           p3 = ("0x" substr($4, 11, 4)) + 0;
           printf "%s,%s,%s,%s,%d,%d\n", $1, $2, $3, p1, p2, p3;
         }'

对于您的样本输入数据,它会产生:

123711184642,02,3583090366663629,639f02,292,14292
123715942138,01,3538710295145500,639f02,45014,50755
123711616258,02,3548370476972758,639f02,72,22322

字符串连接'0x'加上4位十六进制,然后添加0强制awk将数字视为十六进制。

您可以将其简化为:

awk -F, '{ p1 =      substr($4,  1, 6);
           p2 = "0x" substr($4,  7, 4);
           p3 = "0x" substr($4, 11, 4);
           printf "%s,%s,%s,%s,%d,%d\n", $1, $2, $3, p1, p2, p3;
         }'

当呈现为printf()%d格式时,前缀为0x的字符串将被强制为整数。


上面的代码与MacOS X 10.6.5(版本20070501)上的原生awk完美配合;遗憾的是,它不适用于GNU gawk 3.1.7。根据POSIX,似乎是允许的行为(参见下面的评论)。但是,gawk有一个非标准函数strtonum,可以用来强制它正确执行 - 遗憾的是,必须进行大肆宣传。

gawk -F, '{ p1 =      substr($4,  1, 6);
            p2 = "0x" substr($4,  7, 4);
            p3 = "0x" substr($4, 11, 4);
            printf "%s,%s,%s,%s,%d,%d\n", $1, $2, $3, p1, strtonum(p2), strtonum(p3);
          }'

答案 2 :(得分:6)

按AWK

这个答案专注于展示如何通过awk进行转换。

根据GNU Awk User's Guide,建议不要使用--non-decimal-data进行gawk。使用strtonum()不可移植。

在以下示例中,将转换每条记录的第一个单词。

按用户定义的功能

最便携的转换方式是用户定义的awk函数[reference]:

function parsehex(V,OUT)
{
    if(V ~ /^0x/)  V=substr(V,3);

    for(N=1; N<=length(V); N++)
        OUT=(OUT*16) + H[substr(V, N, 1)]

    return(OUT)
}

BEGIN { for(N=0; N<16; N++)
        {  H[sprintf("%x",N)]=N; H[sprintf("%X",N)]=N } }

{ print parsehex($1) }

通过调用shell&gt;的printf

你可以用这个

awk '{cmd="printf %d 0x" $1; cmd | getline decimal; close(cmd); print decimal}'

但它相对较慢。如果您要转换许多换行符分隔的十六进制数字,则以下一个更快:

awk 'BEGIN{cmd="printf \"%d\n\""}{cmd=cmd " 0x" $1}END{while ((cmd | getline dec) > 0) { print dec }; close(cmd)}'

如果为单个printf命令添加了很多参数,则可能会出现问题。

在Linux中

根据我的经验,以下适用于Linux:

awk -Wposix '{printf("%d\n","0x" $1)}'

我在Ubuntu Linux 14.04中用gawk,mawk和original-awk测试过它。通过original-awk,该命令会显示一条警告消息,但您可以通过shell中的重定向指令2>/dev/null将其隐藏。如果您不想这样做,可以在原始awk的情况下删除-Wposix

awk $(awk -Wversion >/dev/null 2>&1 && printf -- "-Wposix") '{printf("%d\n","0x" $1)}'

(在Bash 4中,您可以将>/dev/null 2>&1替换为&>/dev/null

注意:-Wposix技巧可能不适用于OS X和某些BSD OS变体中使用的nawk。

答案 3 :(得分:0)

cat all_info_List.csv| awk 'BEGIN {FS="|"}{print $21}'| awk 'BEGIN {FS=":"}{p1=$1":"$2":"$3":"$4":"$5":";  p2 = strtonum("0x"$6); printf("%s%02X\n",p1,p2+1) }'

上面的命令打印“all_info_List.csv”的内容,这是一个字段分隔符为“|”的文件。 然后取字段21(MAC地址)并使用字段分隔符“:”拆分它。 它将变量“p1”分配给每个mac地址的前5个字节,所以如果我们有这个mac地址:“11:22:33:44:55:66”,p1将是: “11:22:33:44:55:”。 p2被赋予最后一个字节的十进制值:“0x66”会将“102”十进制分配给p2。 最后,我正在使用printf加入p1p2,同时在向其添加一个后将p2转换回十六进制。

答案 4 :(得分:0)

这可能适合你(GNU sed&amp; printf):

sed -r 's/(....)(....)$/ 0x\1 0x\2/;s/.*/printf "%s,%d,%d" &/e' file

拆分最后八个字符,并使用十六进制标识符在字段前面添加空格,然后使用printf评估整行。

答案 5 :(得分:0)

printf "%d\n", strtonum( "0x"$1 )"

答案 6 :(得分:-1)

Perl版本,带着@Jonathan的小费:

perl -F, -lane '$p1 = substr($F[3], 0, 6); $p2 = substr($F[3], 6, 4); $p3 = substr($F[3], 10, 4); printf "%s,%s,%s,%s,%d,%d\n", @F[0..2], $p1, hex($p2), hex($p3)' file

-a启用自动分割模式,填充@F数组
-F,将autosplit分隔符更改为,(默认为空格)
substr()索引比其awk等价物少1,因为Perl数组从0开始。

输出:

123711184642,02,3583090366663629,639f02,292,14292
123715942138,01,3538710295145500,639f02,45014,50755
123711616258,02,3548370476972758,639f02,72,22322