在字符串中全局修改并替换 $1 (awk) 或 \1 (sed) 值从十进制到十六进制?

在字符串中全局修改并替换 $1 (awk) 或 \1 (sed) 值从十进制到十六进制?

是否可以在字符串中全局修改和替换 $1 (awk) 或 \1 (sed) 值从十进制到十六进制?该字符串可能包含任何十进制值,需要对其进行修改并替换为其等效的十六进制值。

awk 示例:

echo "/Test-Test-Test-Test-Test/Test/Test/" | awk '{gsub("&#([0-9]+);", $1, $0); print}'

sed 示例:

echo "/Test-Test-Test-Test-Test/Test/Test/" | sed -E 's/&#([0-9]+);/$(printf "%X" \1)/g;'

echo "/Test-Test-Test-Test-Test/Test/Test/" | sed -E 's/&#([0-9]+);/$(echo "obase=16; \1" | bc)/g;'

我尝试使用 printf "%X" 和 bc 进行子执行和管道操作,但无法将两者结合起来以进行十进制到十六进制的修改和替换。

预期输出:

%2FTest%2DTest%2DTest%2DTest%2DTest%2FTest%2FTest%2F

非常感谢您的帮助。

答案1

使用 GNU 时awkRecordS分隔符可以是正则表达式,它匹配的内容存储在RT

gawk -v RS='&#[0-9]+;' -v ORS= '1;RT{printf("%%%02X", substr(RT,3))}'

就我个人而言,我会使用perl

perl -pe 's{&#(\d+);}{sprintf "%%%02X", $1}ge'

也可以看看:

perl -MURI::Escape -MHTML::Entities -lpe '$_ = uri_escape decode_entities $_'

这里给出:

%2FTest-Test-Test-Test-Test%2FTest%2FTest%2F

因为连字符不需要在 URI 中进行编码。它还会负责转换%%25、空格%20&%26等等。

还有一个问题是如何处理非 ASCII 字符(上面的字符)?如果它们应该转换为 UTF-8 编码的 URI 编码,例如€(€, U+20AC, €) 转换为%E2%82%AC(该字符的 UTF-8 编码的 3 个字节),那么应该是:

perl  -MURI::Escape -MHTML::Entities -lpe '$_ = uri_escape_utf8 decode_entities $_'

使用uri_escape,您将获得 ISO8859-1(又名 latin1)编码,在当今时代,这种编码不太可能是您想要的(并且仅限于最多字符ÿ)。其他解决方案将转换€%20AC例如,这绝对是错误的。

答案2

使用 GNU awk 作为第三个参数match()

$ echo "/Test-Test-Test-Test-Test/Test/Test/" |
awk '{
    while ( match($0,/(.*)&#([0-9]+);(.*)/,a) ) {
        $0 = a[1] sprintf("%%%02X",a[2]) a[3]
    }
    print
}'
%2FTest%2DTest%2DTest%2DTest%2DTest%2FTest%2FTest%2F

否则,在每个 Unix 机器上的任何 shell 中使用任何 awk:

$ echo "/Test-Test-Test-Test-Test/Test/Test/" |
awk '{
    while ( match($0,/&#[0-9]+;/) ) {
        $0 = substr($0,1,RSTART-1) sprintf("%%%02X",substr($0,RSTART+2,RLENGTH-3)) substr($0,RSTART+RLENGTH)
    }
    print
}'
%2FTest%2DTest%2DTest%2DTest%2DTest%2FTest%2FTest%2F

答案3

使用 GNU sed,它在 s/// 命令上有 /e 修饰符,我们可以这样做,如下所示:

$ sed -E ":a;s/(.*)&#([0-9]+);(.*)/printf %s '\\1' \"\$(dc -e '37an16o\\2f')\" '\\3'/e;ta" file
  • 扩展正则表达式模式下的 GNU sed -E
  • GNU dc 将小数转换为十六进制。
  • 然后我们通过 t 命令重复替换。

如果您的 GNU sed 尚不支持 s/// 命令的 /e 修饰符,我们可以将输入行转换为 GNU dc 代码块并将其通过管道传输到 dc:

< file \
sed -E '1i\
16o
  s/&#([0-9]+);/\n37an\1n\n/g
  s/([^\n]*)/[&]/g
  s/([^\n]*\n){2}/&x/g
  s/\nx/x /g;y/\n/n/
  s/$/pc/;s/.*/[&]x/
' | dc

它本质上做的是:

  • 输出为十六进制 (16o)
  • 转小数 -> %HEX 等值
  • 用换行符分隔十六进制部分
  • 将这些非换行符块转换为 dc 字符串,稍后传递给 dc 执行。

相关内容