2009-11-25 53 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