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

在awk或sed中将十六进制转换为十进制,sed,awk,decimal,hex,Sed,Awk,Decimal,Hex,我有一个以逗号分隔的数字列表: 123711184642,02,3583090366663629,639f02012437d4 123715942138,01,3538710295145500,639f02afd6c643 123711616258,02,3548370476972758,639f0200485732 我需要将第三列拆分为三列,如下所示: 123711184642,02,3583090366663629,639f02,0124,37d4 123715942138,01,3538

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

123711184642,02,3583090366663629,639f02012437d4
123715942138,01,3538710295145500,639f02afd6c643
123711616258,02,3548370476972758,639f0200485732
我需要将第三列拆分为三列,如下所示:

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
123711184642,02,3583090366663629,639f02,292,14292
123715942138,01,3538710295145500,639f02,45014,50755
123711616258,02,3548370476972758,639f02,72,22322
这似乎有效:

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
有一个非标准函数
strotnum
,可以用来强迫它正确执行-遗憾的是,强迫是必要的

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);
          }'

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

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
    }'
如果需要的话,我加入了一种相当扭曲的方式来添加选项

编辑

只是为了见鬼,这里有一个纯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
“${line[*]:0:3}”
(引用的
*
)的工作原理类似于AWK的
OFS
,因为它会导致在输出时在数组元素之间插入Bash的
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
不幸的是,Bash不允许
printf-v
(类似于
sprintf()
)对数组元素进行赋值,因此
printf-v“line[6]”不起作用

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

printf -v 'line[6]' '%d' "0x${line[3]:10:4}"
需要在数组引用周围加引号,以防止可能的文件名匹配。如果当前目录中存在名为“line6”的文件且引用未被引用,则将创建(或更新)一个名为
line6
的变量,其中包含printf输出。关于该文件的任何其他内容,例如其内容,都不会起作用。只有名字-而且只有切线

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地址),并使用字段分隔符“:”将其拆分。 它将每个mac地址的前5个字节分配给变量“
p1
”,因此如果我们有这个mac地址:“11:22:33:44:55:66”,
p1
将是:“11:22:33:44:55:”。
p2
被分配最后一个字节的十进制值:“0x66”将为
p2
分配“102”十进制值。 最后,我使用
printf
连接
p1
p2
,同时将
p2
转换回十六进制,然后再向其中添加一个 这个答案集中于展示如何通过可移植的awk进行转换

根据,不建议使用
--非十进制数据
进行gawk。并且使用strtonum()
是不可移植的

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

按用户定义的函数 进行转换的最方便的方法是使用用户定义的awk函数[]:

但这是相对缓慢的。如果要转换多个换行分隔的十六进制数,则下面的转换速度更快:

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)}'
我在UbuntuLinux14.04中通过gawk、mawk和原始awk对其进行了测试。通过原始awk,该命令显示一条警告消息,但您可以通过shell中的重定向指令
2>/dev/null
将其隐藏。如果您不想这样做,您可以在原始awk的情况下剥离
-Wposix
,如下所示:

awk $(awk -Wversion >/dev/null 2>&1 && printf -- "-Wposix") '{printf("%d\n","0x" $1)}'
(在Bash4中,您可以用
&>/dev/null
替换
>/dev/null>&1

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

这可能适用于您(GNU-sed&printf):


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

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,
将自动拆分分隔符更改为
(默认为空白)
substr()
索引比它们的awk等价物少1,因为Perl数组从0开始

输出:


我在最后两列中得到零。123711184642,023583090366663629639F02,0,0 123715942138,01358710295145500639F02,0,0 123711616258,02354837047972758639F02,0,0哪个平台上的
awk
?我正在使用MacOS X 10.6.5及其awk版本20070501;当我使用Gawk3.1.7时,它会给出零。这值得向GNU报告一次错误。我将研究一种变通方法……我在使用GNU Awk 3.1的Redhat Linux 2.6和SunOS 5.10上获得了类似的结果。5@bernie:如果使用
--非十进制数据
选项,第一个版本将与
gawk
一起使用。POSIX说它是特定于实现的。@bernie:我删除了一个错误的
sprintf
到数组的printf-v
我相信bash4.1中添加了元素,做得很好;值得将
2>/dev/null
添加到
awk--version
,因为
mawk
将打印带有
--version
的错误消息。根据建议,不建议使用
--non-decimal data
。或者,添加
-Wposix
选项似乎适用于所有适用于Ubuntu Linux的awk实现,即mawk、gawk和original-awk。尽管
-Wposix
可能不适用于OSX和某些BSD操作系统中使用的nawk,如本.s中所述
sed -r 's/(....)(....)$/ 0x\1 0x\2/;s/.*/printf "%s,%d,%d" &/e' file
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
123711184642,02,3583090366663629,639f02,292,14292
123715942138,01,3538710295145500,639f02,45014,50755
123711616258,02,3548370476972758,639f02,72,22322
printf "%d\n", strtonum( "0x"$1 )"