【问题标题】:update xml node using sed [duplicate]使用 sed 更新 xml 节点
【发布时间】:2022-01-01 21:41:32
【问题描述】:
I have an xml that looks something like this


<?xml version="1.0" encoding="UTF-8"?>
<project
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <version>1</version>
    </parent>
    <version>5</version>
    <properties>
        <test.version>10</test.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <version>${test.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

我的任务是

  1. 仅更改父标签内的版本
  2. 更改属性标签内的 test.version。

我生成的 xml 应该是这样的

<parent>
    <version>2</version>                 //changed here
</parent>

<version>5<version>

<properties>
    <test.version>20</test.version>     //changed here
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <version>${test.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

我已经尝试使用 sed 来满足第二个要求

sed -i '/<test.version>/,/<\/test.version>/s/10/20/' "filename"

假设所有的 xml 都在一个文件中

上述 sed 命令的问题是它可以用 20 替换 10。但我想用 20 替换任何数字。如何做到这一点。

当我尝试使用与上述相同的 sed 命令时,第一个要求正在更改所有匹配版本。我只想更改父级内部的版本。再次在这里,我想将父/版本中的任何内容更改为 30

【问题讨论】:

  • Don't Parse XML/HTML With Regex.。我建议使用 XML/HTML 解析器(xmlstarlet、xmllint ...)。
  • @Cyrus 问题是无法在我的 shell 脚本工作的地方安装任何其他命令。我只需要使用像 sed 这样的现有命令来操作 xml
  • 当你有一个有效的XML,请点击这里,开始阅读一些问答:stackoverflow.com/questions/tagged/xmlstarlet
  • “看起来像这样”——是的,问题就在这里。正则表达式是错误的工具,当格式稍有变化时会意外中断。
  • 您是否安装了xsltproc,它基于非常标准的libxml 库? Python 怎么样?

标签: xml bash shell sed xml-namespaces


【解决方案1】:

如果无法安装xmlstarlet,请使用 Perl 及其 XML 解析器更新节点。

将我的解析器放在一个名为xmlupdate.pl的文件中:

#!/usr/bin/perl
use strict;
use warnings;
use XML::LibXML;

my $key = $ARGV[0];
my $value = $ARGV[1];
my $file= $ARGV[2];

my $parser = XML::LibXML->new();
my $cyrus = $parser->parse_file($file);

for my $data ( $cyrus->findnodes($key . '/text()') ) {
  $data->setData($value);
}

print $cyrus->toString;

语法:perl xmlupdate.pl XML-path new_value filename

xmlupdate.pl 将其输出写入标准输出。


然后使用它从文件file.xml 中将//project/parent/version 更新为值2

perl xmlupdate.pl '//*[name()="project"]/*[name()="parent"]/*[name()="version"]' '2' 'file.xml' > file_tmp.xml
mv file_tmp.xml file.xml

并将//project//properties/test.version 更新为值20

perl file.pl '//*[name()="project"]/*[name()="properties"]/*[name()="test.version"]' '20' 'file.xml' > file_tmp.xml
mv file_tmp.xml file.xml

提示:您的文件使用namespaces。如果没有命名空间,您可以只使用//project/parent/version//project//properties/test.version

【讨论】:

    【解决方案2】:

    使用 GNU awk:

    gawk -v val=2 -v RS="</project>" 'match($0, /(.*<parent>\s*<version>\s*)[0-9]*(\s*<\/version>.*)/, a) { t = a[1] val a[2] } END { if (t) print t RS > FILENAME }' file.xml
    gawk -v val=20 -v RS="</project>" 'match($0, /(.*<properties>\s*<test\.version>\s*)[0-9]*(\s*<\/test\.version>.*)/, a) { t = a[1] val a[2] } END { if (t) print t RS > FILENAME }' file.xml
    

    【讨论】:

      【解决方案3】:

      如果我们假设标签的行号不会改变:

      父版本行号 = 6

      测试版本的行号 = 10

      单行

      sed -i "6s/[0-9]/<new_parent_value>/;10s/[0-9][0-9]/<new_test_value>/" file.xml
      

      单行

      sed -i "6s/[0-9]/<new_parent_value>/" file.xml
      sed -i "10s/[0-9][0-9]/<new_test_value>/" file.xml
      

      写期望值而不是

      如果使用带有输入的脚本适合您:

      #!/bin/bash
      
      # take user input for new values
      echo "Enter new parent version value:"
      read parverval
      echo "Enter new test version value:"
      read tesverval
      
      # find the lines
      pver=`grep -n "<parent>" file.xml | cut -d: -f1`
      ((pver=$pver+1))
      tver=`grep -n "<test.version>" file.xml | cut -d: -f1`
      
      # make the changes
      echo "Changing parent version to $parverval "
      sed -i "${pver}s/[0-9]/$parverval/p" file.xml
      
      echo "Changing test version value to $tesverval "
      sed -i "${tver}s/[0-9][0-9]/$tesverval/p" file.xml
      

      这将接受新值的输入。

      如果您不想接受输入:

      #!/bin/bash
      
      parverval="<new_parent_value>"
      tesverval="<new_test_value>" 
      # find the lines
      pver=`grep -n "<parent>" file.xml | cut -d: -f1`
      ((pver=$pver+1))
      tver=`grep -n "<test.version>" file.xml | cut -d: -f1`
      
      sed -i "${pver}s/[0-9]/$parverval/p" file.xml
      sed -i "${tver}s/[0-9][0-9]/$tesverval/p" file.xml
      

      注意事项

      -保持“”和它的“”标签序列化。

      -位数

      【讨论】:

        猜你喜欢
        • 2011-06-12
        • 1970-01-01
        • 2022-10-04
        • 1970-01-01
        • 1970-01-01
        • 2013-05-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多