在 XML 文件中查找和递增数字

tar*_*che 10 xml perl awk

我试图在XML文件中搜索一个字符串,将1紧随其后的数字递增,然后将更改保存回同一个文件。这个字符串只有一个实例。

我的文件看起来像这样:

        <attribute>
                <name>test</name>
                <type>java.lang.String</type>
                <value>node1-3</value>
        </attribute>
Run Code Online (Sandbox Code Playgroud)

我正在尝试更改3(在 node1- 之后)并在1每次运行命令时增加它。我尝试了以下 sed,将该行分成4几部分,然后用这些4部分替换它,再加上一个增量。不幸的是,它似乎没有做任何事情:

 sed -i -r -e 's/(.*)(\node1-)([0-9]+)(.*)/echo "\1\2$((\3+1))\4"/g' filepath
Run Code Online (Sandbox Code Playgroud)

我也试过awk,这似乎让我在某个地方,但我不确定如何将行的后半部分附加到 (

awk '{FS=OFS="-" }/node1/{$2+=1}1' filepath
Run Code Online (Sandbox Code Playgroud)

最后,我尝试了 perl,但它增加了错误的数字,从node1node2,而不是在破折号之后:

perl -i -pe '/node1-/ && s/(\d+)(.*)/$1+1 . $2/e' filepath
Run Code Online (Sandbox Code Playgroud)

我是这些命令的新手,对我的正则表达式不太熟悉。我试图让这个命令工作,以便我可以在我正在编写的 bash 脚本中使用它。最好的方法是什么?哪个命令比另一个有优势?我想要一个1行命令来简化以后的事情。

zdi*_*dim 9

使用 XML 解析器处理文件。这在各方面都比用正则表达式破解它要好。

use warnings;
use strict;

use XML::LibXML;

my $file = shift // die "Usage: $0 file\n";

my $doc = XML::LibXML->load_xml(location => $file);

my ($node) = $doc->findnodes('//value');

my $new_value = $node->to_literal =~ s/node1\-\K([0-9]+)/1+$1/er;

$node->removeChildNodes();
$node->appendText($new_value);

$doc->toFile('new_' . $file);   # or just $file to overwrite
Run Code Online (Sandbox Code Playgroud)

$file完全测试后,将输出文件名更改为输入名称 ( ) 以进行覆盖。

像上面那样删除和添加节点是更改 XML 对象的一种方法。

或者,使用setData的第一个孩子

$node->firstChild->setData($new_value);
Run Code Online (Sandbox Code Playgroud)

wheresetData可用于类型为text,cdata或的节点comment

或者,搜索文本,然后直接使用文本节点

my ($tnode) = $doc->findnodes('//value/text()');

my $new_value = $tnode =~ s/node1\-\K([0-9]+)/1+$1/er;

$tnode->setData($new_value);

print $doc->toString;
Run Code Online (Sandbox Code Playgroud)

还有更多。使用什么方法取决于所有需要做的事情。如果唯一的工作确实只是编辑该文本,那么最简单的方法可能是获取一个text节点。


bri*_*foy 6

这是一个使用 Perl 的XML::Twig的示例。基本上,您为节点创建一个处理程序,然后在该处理程序中执行您需要执行的任何操作。您可以查看当前文本,创建一个新字符串,并将节点文本设置为该字符串。一开始有点吓人,但是一旦习惯了它就非常强大。与其他 Perl XML 解析器相比,我更喜欢这个,但对于非常简单的事情,它可能不是最好的工具:

#!perl
use v5.26;

use XML::Twig;

my $xml = <<~"XML";
    <attribute>
        <name>test</name>
        <type>java.lang.String</type>
        <value>node1-3</value>
    </attribute>
    XML

my $twig = XML::Twig->new(
    pretty_print  => 'indented',
    twig_handlers => {
        # the key is the name of the node you want to process
        value => sub {
            # each handler gets the twig and the current node
            my( $t, $node ) = @_;
            my $current = $node->text;
            # how you modify the text is not important. This
            # is just a Perl substitution that does not modify
            # the original but returns the new string
            my $next = $current =~ s/(\d+)\z/ $1 + 1 /re;
            $node->set_text( $next );
            }
        }
    );
$twig->parse( $xml );
my $updated_xml = $twig->sprint;

say $updated_xml;
Run Code Online (Sandbox Code Playgroud)

XML::Twig需要阅读的其他一些内容:


bri*_*foy 5

只是为了好玩,我使用 Perl 的Mojo::DOM使用 CSS 选择器来完成相同的任务。这不像XML::Twig(没有流解析!)那么强大,但对于简单的事情,它可以很好地解决:

#!perl
use v5.26;

use Mojo::DOM;

my $xml = <<~"XML";
    <attribute>
        <name>test</name>
        <type>java.lang.String</type>
        <value>node1-3</value>
    </attribute>
    XML

my $dom = Mojo::DOM->new( $xml );
my $node = $dom->at( 'attribute value' ); # CSS Selector

my $current = $node->text;
say "Current text is $current";

# how you change the value is up to you. This line is
# just how I did it.
my $next = $current =~ s/(\d+)\z/ $1 + 1 /re;
say "Next text is $next";

$node->content( $next );

say $dom;
Run Code Online (Sandbox Code Playgroud)

它不像单线那么糟糕,但它有点冗长。在-0777使段落模式来思乐普在第一行读取的所有内容(有在最后的文件名的命令行参数):

$ perl -MMojo::DOM -0777 -E '$d=Mojo::DOM->new(<>); $n=$d->at(q(attribute value)); $n->content($n->text =~ s/(\d+)\z/$1+1/er); say $d' text.xml
<attribute>
    <name>test</name>
    <type>java.lang.String</type>
    <value>node1-4</value>
</attribute>
Run Code Online (Sandbox Code Playgroud)

Mojo 有一个ojo模块(所以, with -M, spells Mojo),它以声明变量为代价使这稍微简单一些。这x()是一个快捷方式Mojo::DOM->new()

$ perl -Mojo -0777 -E 'my $d=x(<>); my $n=$d->at(q(attribute value)); $n->content($n->text =~ s/(\d+)\z/$1+1/er); say $d' text.xml
<attribute>
    <name>test</name>
    <type>java.lang.String</type>
    <value>node1-4</value>
</attribute>
Run Code Online (Sandbox Code Playgroud)


Ope*_*uce 3

您可以对节点行的最后部分进行硬编码吗?

$ awk '{FS=OFS="-" }/node1/{$2+=1; print $1 "-" $2 "</value>"} $0 !~ /node1/ {print}' file
  <attribute>
          <name>test</name>
          <type>java.lang.String</type>
          <value>node1-4</value>
  </attribute>
Run Code Online (Sandbox Code Playgroud)