我试图在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,但它增加了错误的数字,从node1到node2,而不是在破折号之后:
perl -i -pe '/node1-/ && s/(\d+)(.*)/$1+1 . $2/e' filepath
Run Code Online (Sandbox Code Playgroud)
我是这些命令的新手,对我的正则表达式不太熟悉。我试图让这个命令工作,以便我可以在我正在编写的 bash 脚本中使用它。最好的方法是什么?哪个命令比另一个有优势?我想要一个1行命令来简化以后的事情。
使用 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节点。
这是一个使用 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需要阅读的其他一些内容:
只是为了好玩,我使用 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)
您可以对节点行的最后部分进行硬编码吗?
$ 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)