我需要查找并替换特定xml元素的值。条件如下:
我的测试xml看起来像这样:
<somenode name="node1">
<some></some>
<enabled>0</enabled>
<some></some>
</somenode>
<someothernode name="node2">
<some></some>
<enabled>0</enabled>
<some></some>
</someothernode>
<somenode name="node3">
<some></some>
<enabled>0</enabled>
<some></some>
</somenode>
我希望第一个和第三个启用的元素会被更改。到目前为止,我已设法编写此sed命令:
sed -n "1h;1!H;${;g;s|\(<somenode [^>]*>\)\(.*\)\(<enabled>\s*\)0\(\s*</enabled>\)\(.*</somenode>\)|\1\2\3 1 \4\5|g;p;}" test.xml
但它只改变了最后一个,我相信这是由于贪婪的比赛。 任何帮助将不胜感激。
答案 0 :(得分:4)
尝试使用正则表达式来解析XML通常是一个糟糕的主意。请参阅前面的讨论,例如Parsing XML with REGEX in Java。 (实际上你的XML格式不正确,因为它没有一个根元素)。有许多不同的(免费)XML引擎用于解析和操作几乎所有语言的XML,我建议你使用其中一种。
答案 1 :(得分:4)
如果可能,请使用xmlstarlet:
echo '
<root>
<somenode name="node1">
<some></some>
<enabled>0</enabled>
<some></some>
</somenode>
<someothernode name="node2">
<some></some>
<enabled>0</enabled>
<some></some>
</someothernode>
<somenode name="node3">
<some></some>
<enabled>0</enabled>
<some></some>
</somenode>
</root>
' > testfile.xml
xml val testfile.xml
xml el -v testfile.xml
xml ed --help
# version 1
xml ed -u "//somenode[1]/enabled" -v '1' \
-u "//somenode[2]/enabled" -v '1' \
testfile.xml
# version 2 (-L for in-place editing; xmlstarlet v1.0.2)
xml ed -L -u "//somenode[@name='node1']/enabled" -v '1' \
-u "//somenode[@name='node3']/enabled" -v '1' \
testfile.xml
答案 2 :(得分:2)
忘记sed
复杂的多行处理。严重。
如果您不愿意使用正确的XML工具,至少使用具有正确分支语句的标准字符串处理工具: - )
如果您可以保证您的文件格式化方式,则可以使用以下内容:
pax> echo '<somenode name="node1">
<some></some>
<enabled>0</enabled>
<some></some>
</somenode>
<someothernode name="node2">
<some></some>
<enabled>0</enabled>
<some></some>
</someothernode>
<somenode name="node3">
<some></some>
<enabled>0</enabled>
<some></some>
</somenode>
' | awk '
BEGIN {s = 0}
/^<somenode / {s=1}
/^<\/somenode>/ {s=0}
/^ <enabled>0<\/enabled>/ {if (s==1) {$0=" <enabled>1</enabled>"}}
{print}
'
得到:
<somenode name="node1">
<some></some>
<enabled>1</enabled>
<some></some>
</somenode>
<someothernode name="node2">
<some></some>
<enabled>0</enabled>
<some></some>
</someothernode>
<somenode name="node3">
<some></some>
<enabled>1</enabled>
<some></some>
</somenode>
这种方法的问题在于它无法处理可能是完全有效的XML文件。此特定版本具有某些限制,例如:
这就是为什么最好使用专门为这项工作而构建的工具。但是,如果您只是想快速入侵并且文件格式在您的控制之下,那么可以使用awk
(或perl
或python
或您的其他快速和脏选择脚本工具)。
答案 3 :(得分:2)
其他人已经解释了为什么通常not a good idea使用正则表达式处理XML。
考虑到所有这一切,这里的sed
程序替换 foo 与 bar 匹配的文本 bar 匹配 start 和结束 (包括):
/start/,/end/s/foo/bar/
答案 4 :(得分:0)
你可以使用gawk
awk -vRS= '/somenode/{
$0=gensub("(.*<enabled>)([01])(</enabled>.*)", "\\11\\3","g",$0)
}1' file
输出
$ ./shell.sh
<somenode name="node1">
<some></some>
<enabled>1</enabled>
<some></some>
</somenode>
<someothernode name="node2">
<some></some>
<enabled>0</enabled>
<some></some>
</someothernode>
<somenode name="node3">
<some></some>
<enabled>1</enabled>
<some></some>
</somenode>
答案 5 :(得分:0)
答案 6 :(得分:-1)
从您的描述中可以看出您的要求非常简单,因此如果您不愿意,则无需使用XML解析器/工具。你可以只使用shell(或你喜欢的其他shell工具)
#!/bin/bash
while read -r line
do
case "$line" in
*"<someothernode"* ) flag=0;;
*"<somenode"* )flag=1;;
esac
if [ "$flag" -eq "1" ] ;then
case "$line" in
*"<enabled"* )
echo "${line/<enabled>0/<enabled>1}"
;;
*) echo $line;
esac
else
echo $line
fi
done < "file"