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

ber*_*nie 19 awk hex sed decimal

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

123711184642,02,3583090366663629,639f02012437d4
123715942138,01,3538710295145500,639f02afd6c643
123711616258,02,3548370476972758,639f0200485732
Run Code Online (Sandbox Code Playgroud)

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

123711184642,02,3583090366663629,639f02,0124,37d4
123715942138,01,3538710295145500,639f02,afd6,c643
123711616258,02,3548370476972758,639f02,0048,5732
Run Code Online (Sandbox Code Playgroud)

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

123711184642,02,3583090366663629,639f02,292,14292
123715942138,01,3538710295145500,639f02,45014,50755
123711616258,02,3548370476972758,639f02,72,22322
Run Code Online (Sandbox Code Playgroud)

Pau*_*ce. 24

以下是乔纳森答案的变体:

awk $([[ $(awk --version) = GNU* ]] && echo --non-decimal-data) -F, '
    BEGIN {OFS = FS}
    {
        $6 = sprintf("%d", "0x" substr($4, 11, 4))
        $5 = sprintf("%d", "0x" substr($4,  7, 4))
        $4 = substr($4,  1, 6)
        print
    }'
Run Code Online (Sandbox Code Playgroud)

如果需要的话,我提供了一种相当扭曲的添加--non-decimal-data选项的方法.

编辑

只是为了它,这是纯粹的Bash等价物:

saveIFS=$IFS
IFS=,
while read -r -a line
do
    printf '%s,%s,%d,%d\n' "${line[*]:0:3}" "${line[3]:0:6}" "0x${line[3]:6:4}" "0x${line[3]:10:4}"
done
IFS=$saveIFS
Run Code Online (Sandbox Code Playgroud)

所述"${line[*]:0:3}"(引述*)工作以AWK的类似OFS的,因为它会导致击的IFS(这里是逗号)上输出阵列元件之间的插入.我们可以通过插入如下的数组元素来进一步利用该功能,这与上面的AWK版本更为相似.

saveIFS=$IFS
IFS=,
while read -r -a line
do
    line[6]=$(printf '%d' "0x${line[3]:10:4}")
    line[5]=$(printf '%d' "0x${line[3]:6:4}")
    line[4]=$(printf '%s' "${line[3]:0:6}")
    printf '%s\n' "${line[*]}"
done
IFS=$saveIFS
Run Code Online (Sandbox Code Playgroud)

不幸的是,Bash不允许printf -v(类似于sprintf())对数组元素进行赋值,因此printf -v "line[6]" ...不起作用.

编辑:从Bash 4.1开始,printf -v现在可以对数组元素进行赋值.例:

printf -v 'line[6]' '%d' "0x${line[3]:10:4}"
Run Code Online (Sandbox Code Playgroud)

需要数组引用周围的引号以防止可能的文件名匹配.如果当前目录中存在名为"line6"的文件并且未引用引用,则将line6创建(或更新)包含printf输出的名为的变量.关于文件的其他任何内容,例如其内容,都将起作用.只有名字 - 只有切线.


Jon*_*ler 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;
         }'
Run Code Online (Sandbox Code Playgroud)

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

123711184642,02,3583090366663629,639f02,292,14292
123715942138,01,3538710295145500,639f02,45014,50755
123711616258,02,3548370476972758,639f02,72,22322
Run Code Online (Sandbox Code Playgroud)

字符串连接'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;
         }'
Run Code Online (Sandbox Code Playgroud)

以0x前缀字符串被迫整数呈现给时printf()%d格式.


上面的代码与awkMacOS X 10.6.5(版本20070501)上的本机代码完美搭配; 遗憾的是,它不适用于GNU gawk3.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);
          }'
Run Code Online (Sandbox Code Playgroud)

  • @bernie:如果使用`--non-decimal-data`选项,第一个版本将与`gawk`一起使用. (2认同)

jar*_*rno 7

通过AWK

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

--non-decimal-data根据GNU Awk用户指南,不建议使用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) }
Run Code Online (Sandbox Code Playgroud)

通过调用shell的printf

你可以用它

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

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

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

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

在Linux中

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

awk -Wposix '{printf("%d\n","0x" $1)}'
Run Code Online (Sandbox Code Playgroud)

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

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

(在bash 4,你可以替换>/dev/null 2>&1&>/dev/null)

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

  • 这在“original-awk”中有效,因为它不需要 `-W posix` _除非_它是 gawk;gawk 需要 `-W posix`。这包括 Debian 系统上的 mawk 1.3.3 以及 FreeBSD 7.3 的 awk 20070501 和 FreeBSD 11.2 的 awk 20121220。 (2认同)