使用 sed 替换 XML 标记内容的问题

使用 sed 替换 XML 标记内容的问题

我有以下 XML 片段:

<value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">+380554446363</value>
<value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">+380554446364</value>
<value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">+380554446365</value>

我尝试<value>使用以下命令将标签内容替换为其 SHA-1 哈希值:

cat test.xml | sed "s/>[+]\([0-9][0-9]*\)<\/value>/>+$(echo \\1 | sha1sum | cut -f1 -d' ')<\/value>/g"

它会失败,因为用相同的错误值替换所有找到的案例。

预期的:

<value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">34df370575e3528b31daef8633cb539119a3b028</value>
<value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">d93767c769fd51bcf9eb25f95932559b24bae812</value>
<value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">20338c1f048bed553b6cce76eaf1d388ba7686f5</value>

得到:

<value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">+cbcac786fef5abeb39fe473ab6abe554978a8156</value>
<value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">+cbcac786fef5abeb39fe473ab6abe554978a8156</value>
<value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">+cbcac786fef5abeb39fe473ab6abe554978a8156</value>

我可能做错了什么? TIA。

答案1

不要使用sed或编辑 XML awk。使用适当的xml解析器,例如 python 的xml.etree

input.xml

<values>
    <value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">+380554446363</value>
    <value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">+380554446364</value>
    <value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">+380554446365</value>
</values>
#!/usr/bin/env python3
import hashlib
import xml.etree.ElementTree as ET

tree = ET.parse('input.xml')
root = tree.getroot()

for value in root:
    value.text = hashlib.sha256(value.text.encode('utf-8')).hexdigest()


tree.write('output.xml')

output.xml

<values>
    <value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">4b2bdff20d17dc4ae7ad99937399530b39bd7a63f7348375c547d01565c11898</value>
    <value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">8d3b1452d83ef803f8043eaddbe2fc58b0fd42c8ad5abc554e78796548f75ddb</value>
    <value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">06bd986417875e536401306180c51b7ded4eeab179933e0026a103bc8dc9eee7</value>
</values>

答案2

正在sha1sum评估常量字符串“\\1”的 SHA-1,而不是第一个 SED 正则表达式匹配:

$ echo \\1 | sha1sum
cbcac786fef5abeb39fe473ab6abe554978a8156  -

shell 在执行命令(在本例中为 sed)之前执行所有各种扩展(例如命令替换)。因此,shell 展开

cat test.xml | sed "s/>[+]\([0-9][0-9]*\)<\/value>/>+$(echo \\1 | sha1sum | cut -f1 -d' ')<\/value>/g"

cat test.xml | sed "s/>[+]\([0-9][0-9]*\)<\/value>/>+cbcac786fef5abeb39fe473ab6abe554978a8156<\/value>/g"

然后它运行两个进程,一个正在运行

cat test.xml

另一个正在运行

sed "s/>[+]\([0-9][0-9]*\)<\/value>/>+cbcac786fef5abeb39fe473ab6abe554978a8156<\/value>/g"

第一个进程的 STDOUT 通过管道传输到第二个进程的 STDIN。

为了使您想要做的事情发挥作用,sed必须能够从 内部执行其他可执行文件sed。我不相信sed可以做到这一点,所以你必须以其他方式做到这一点。

您可以使用 来完成此操作sed,例如这是一种方法

for a in `cat test.xml | sed -E 's,^.*>(\+[0-9]+)<\/value>$,\1,'`; do echo "$a" | sha1sum | cut -f1 -d' '; done >2nd
cat test.xml | sed -E 's,>\+[0-9]+</value>$,>,' >1st
paste -d '' 1st 2nd | sed -E 's,$,</value>,'

您也没有在第一个匹配字符串的括号中包含“+” sed,根据您作为预期结果提供的 SHA1 和,我相信您希望包含“+”,所以我也更正了这一点。

答案3

GNU sed 替换命令有一个/e 修饰符,它作为 vash 代码执行,即 s/// 的 rhs,又名修改后的模式空间。

sed -E "
  s/'/&\\\\&&/g
  s:(<.*>)([+][0-9]+)(</value>.*):printf '%s%s%s' '\\1' \"\$(echo '\\2'|sha1sum|cut -d' ' -f1)\" '\\3':e
" test.xml

<value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">34df370575e3528b31daef8633cb539119a3b028</value>
<value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">d93767c769fd51bcf9eb25f95932559b24bae812</value>
<value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">20338c1f048bed553b6cce76eaf1d388ba7686f5</value>

答案4

如果您使用的系统具有编译为crypto 扩大你可以说的功能:

xmlstarlet sel -N crypto='http://exslt.org/crypto' \
  -t -m '//value' -e '{name()}' -c '@*' -v 'crypto:sha1(.)' -b -n \
file.xml

输出:

<value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">3bacaac1e104d7bfde5ff462d461d3b4b917c037</value>
<value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">808b3e7bb8c8c7c06c328a082bac64205837531b</value>
<value id="1" creatorId="0" creationTime="1639487132" expirationTime="1639573532">8c31811a3b8d816f719ad5c78b58810b9b6cd4b7</value>

其中校验和与所需的输出不同,因为它们不是根据包括换行符的文本计算的。


xmlstarlet transform --show-ext 2>&1 | grep crypto在我的最终输出中运行:

{http://exslt.org/crypto}md4
{http://exslt.org/crypto}sha1
{http://exslt.org/crypto}md5
{http://exslt.org/crypto}rc4_decrypt
{http://exslt.org/crypto}rc4_encrypt

相关内容