使用 sed 将长数字转换为十六进制

使用 sed 将长数字转换为十六进制

我正在尝试创建一个sed命令,以便将长度超过 3 位的数字转换为十六进制。即像这样的字符串124 3275 7535应该产生124 0xccb 0x1d6f.这是我目前拥有的:

sed 's@\([0-9]\{4,\}\)@sh -c "printf 0x%x \1"@ge'

但是当字符串不匹配时,它会尝试将未更改的字符串作为外部命令运行,因此对于上面的示例字符串,我得到

sh:1:124:未找到

我怎样才能实现我想要做的事情(最好仍然使用sed)?

答案1

虽然根据您的问题标题,它不是“with sed”,但如果您从 sed 切换到 perl,您可以使用等效的表达式,例如

perl -p -e 's/\b\d{4,}\b/sprintf "%#x", $&/ge'

这应该允许您或多或少地保留链中的其他表达式。

答案2

GNU 实现的命令e标志是评估ssed模式空间的内容应用替换(成功)后,并用其输出替换模式空间,而不是评估代换

在这里,对于如下输入:

foo 1234 123

您需要替换才能产生包含以下内容的模式空间:

printf %s 'foo '
printf 0x%x 1234
printf %s ' 123'

通过 shell 命令e将其转换为标志。foo 0x3d2 123这并非不可能,例如:

LC_ALL=C sed -E "
  /[0-9]{4}/!b # optimisation
  s/'/&\\\\&/g
  s/[0-9]{4,}/'\nprintf 0x%x &\nprintf %s '/g
  s/.*/printf %s '&'/e"

但这是相当尴尬的,并且意味着每个匹配的输入行运行一个 shell。即使不使用 GNUism,您也可以这样做:

LC_ALL=C sed "
  s/'/&\\\\&/g
  s/[0-9]\{4,\}/'\\
printf 0x%x &\\
printf %s '/g
  s/.*/printf %s '&\\
'/" | sh

哪个会运行 sh

另外,像这样将任意数据评估为 shell 代码往往会让我感到紧张。例如,如果没有上面的 LC_ALL=C,就会构成任意命令执行漏洞。例如,尝试类似以下输出的内容:

printf '0000\200; echo GOTCHA>&2\n'

在 UTF-8 语言环境中。

在这里,您宁愿使用类似的东西perl

perl -pe 's/\d{4,}/sprintf "0x%x", $&/ge'

perle标志更符合您的期望。它确实将替换评估为代码(并且不会像 GNU那样perl每次都启动新的 perl 解释器)。sede

答案3

awk是为确切地这种类型的广谱文本操作。请注意,无需通过管道连接到任何辅助工具。

awk '{ for( fn=1;fn<=NF;fn++ ){
           fmat=(length($fn)>3)?"0x%x":"%s"
           dlim=(fn==NF?"\n":" ")  
           printf( fmat dlim, $fn )}}' <<<'124 3275 7535' 

输出,根据您的示例:

124 0xccb 0x1d6f

答案4

我赞同 Peter.O 在评论中所说的话:这是一种bash方法(它需要在每个数字的末尾):

echo '124 3275 7535 ' | while read -d ' ' x; do [ ${#x} -ge 4 ] && printf "0x%x " $x || printf "%d " $x; done

如果您的输入流在行末尾没有 a (从您的示例来看),那么sed会派上用场:

echo '124 3275 7535' | sed 's/$/ /' | while read -d ' ' x; do [ ${#x} -ge 4 ] && printf "0x%x " $x || printf "%d " $x; done

相关内容