我正在尝试发出一个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)?
GNU 实现e的s命令的标志是在应用替换后(成功)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)
perl的e标志更符合您的期望。它确实evalute的subsitution的perl代码(而不是推出一个新的Perl解释器,每次像GNUsed的e)。
尽管根据您的问题标题它不是“使用 sed”,但如果您从 sed 切换到 perl,您可以使用等效的表达式,例如
perl -p -e 's/\b\d{4,}\b/sprintf "%#x", $&/ge'
Run Code Online (Sandbox Code Playgroud)
这应该允许您或多或少地按原样保留链中的其他表达式。
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)