2009-11-25 54 views
2

我需要查找并替换特定xml元素的值。的条件如下:使用sed查找并替换为xml文件

  • 元件的值使能必须从改为;
  • 启用必须是somenode元素的子

我测试的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 

,但它改变了只有最后一个,我相信这是由于贪婪匹配。 任何帮助,将不胜感激。

+2

我想用XML解析库(Perl,Python,PHP,Ruby)的任何动态语言都比sed更适合这个任务,你使用螺丝刀吃汤的任何特定原因? – 2009-11-25 06:23:51

+0

看看http://stackoverflow.com/questions/91791/grep-and-sed-equivalent-for-xml-command-line-processing - 这里提到了很多工具。 – 2009-11-25 06:37:42

回答

4

尝试使用正则表达式来解析XML通常是一个糟糕的主意。参见前面的讨论,例如Parsing XML with REGEX in Java。 (实际上你的XML格式不正确,因为它没有一个根元素)。几乎所有语言都有许多不同的(免费)XML引擎用于解析和操作XML,我建议您使用其中的一种。

+0

鉴于xml只是一个摘录,我认为不会改变这一点。更普遍的问题是“替换文本中给定单词的所有出现位置,其中该单词在2个其他给定单词之间”。 – NSPKUWCExi2pr8wVoGNk 2009-11-25 06:43:54

+0

这是一个不同的问题,因为解析文本和解析XML不是完全相同的。正如许多海报在页面中提到的,我引用你的XML可能会随着时间的推移而变化,并且还有XML的语法变体(不同的引用字符,空白,CDATA等),这可能会使问题复杂化。对于相同的规范化XML,有几种不同的词法形式。 – 2009-11-25 06:48:42

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> 

用那种方法的问题在于,它不处理有什么可能是完全VAL id XML文件。此特定版本具有一定的局限性,例如:

  • somenode的开始和结束标记必须位于行首。
  • 启用的标签必须在前面有四个空格。 您可以解决这些问题,使其更具灵活性,但是,在您编写脚本处理任意有效的XML输入时,它会变得如此怪异,以至于使用XML会更快转换工具。

这就是为什么最好使用专为工作而构建的工具。但是,如果您只是想快速破解并且文件格式在您的控制之下,那么可以使用awk(或perlpython或您的其他快速和肮脏的脚本工具)。

0

可以使用呆子

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> 
-1

从描述中可以看出,您的需求非常简单,因此如果您不想使用XML解析器/工具,则无需使用它。你可以只使用外壳(或其他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" 
2

其他人已经解释了为什么通常是not a good idea处理XML与正则表达式。

与所有考虑到这一点,这里的sed程序替代文本匹配匹配开始年底(包含地)线间酒吧

/start/,/end/s/foo/bar/ 
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