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

Rus*_*lan 7 sed

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

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

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

sh:1:124:未找到

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

Sté*_*las 7

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

在这里,对于像这样的输入:

foo 1234 123
Run Code Online (Sandbox Code Playgroud)

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

printf %s 'foo '
printf 0x%x 1234
printf %s ' 123'
Run Code Online (Sandbox Code Playgroud)

用于通过 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"
Run Code Online (Sandbox Code Playgroud)

但这很尴尬,意味着每个匹配的输入行运行一个 shell。甚至不用那个 GNUism,你也可以这样做:

LC_ALL=C sed "
  s/'/&\\\\&/g
  s/[0-9]\{4,\}/'\\
printf 0x%x &\\
printf %s '/g
  s/.*/printf %s '&\\
'/" | sh
Run Code Online (Sandbox Code Playgroud)

哪个会运行一个 sh.

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

printf '0000\200; echo GOTCHA>&2\n'
Run Code Online (Sandbox Code Playgroud)

在 UTF-8 语言环境中。

在这里,您宁愿使用以下内容perl

perl -pe 's/\d{4,}/sprintf "0x%x", $&/ge'
Run Code Online (Sandbox Code Playgroud)

perle标志更符合您的期望。它确实evalute的subsitution的perl代码(而不是推出一个新的Perl解释器,每次像GNUsede)。


ste*_*ver 6

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

perl -p -e 's/\b\d{4,}\b/sprintf "%#x", $&/ge'
Run Code Online (Sandbox Code Playgroud)

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

  • @Ruslan 感谢提醒:也许更简单,只需添加词边界锚点,即`\b\d{4,}\b`。我会更新我的答案。 (2认同)

Pet*_*r.O 5

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' 
Run Code Online (Sandbox Code Playgroud)

输出,根据您的示例:

124 0xccb 0x1d6f
Run Code Online (Sandbox Code Playgroud)