我应该用BASH解析这个XML吗?

时间:2013-07-02 11:20:45

标签: xml bash sed awk cygwin

我正试图从这个xml示例中获取

<String Name="descResist">
    <Description><![CDATA["resist_type_chimney"]]></Description>
    <Flags>
        <ParFlg_Child/>
    </Flags>
    <Value><![CDATA["90_min."]]></Value>
</String>

descResist;resist_type_chimney 
descResist;90_min.

因此,基本上我需要提取CDATA内容并使用Name的值来连接它。

其中一个问题是,它并非总是在String标记中......也可能是IntegerTitleBoolean等...... < / p>

我试过这个

$ grep -o "Name=\".*\"\|<\!\[CDATA\[.*\]\]>" file.xml | sed 's/<\!\[CDATA\[\"\(.* \)\"\]\]>/\1/'

给了我

Name="descResist"
resist_type_chimney
90_min.

如何使用Name string的值为下一行添加前缀?

喜欢

Name="descResist"
resist_type_chimney
90_min.
Name="anotherName"
foo_bar
Name="anoooother"
Name="notempty"
bar_foo

它有点复杂。

使用这样的XML也很好吗?还应该有任何嵌套的<tagType Name=...所以我想这应该不是问题。

编辑:我正在研究cygwin,寻找bash / sed / awk简单解决方案。

2 个答案:

答案 0 :(得分:2)

我建议使用解析器。这里有一个使用XML::Twig的{​​{3}}示例。

script.pl的内容:

#!/usr/bin/env perl

use warnings;
use strict;
use XML::Twig;

my $twig = XML::Twig->new(
        twig_handlers => {
                '//*[@Name]' => sub {
                        for my $d ( $_->descendants( '#CDATA' ) ) { 
                                (my $t = $d->text) =~ s/\A"(.*)"\z/$1/; 
                                printf qq|%s;%s\n|, $_->att( 'Name' ), $t; 
                        }   
                },  
        }   
)->parsefile( shift );

像以下一样运行:

perl script.pl xmlfile

产量:

descResist;resist_type_chimney
descResist;90_min.

答案 1 :(得分:2)

试试这个:

#!/bin/bash

Name="InvalidName"
while read line; do
        case "$line" in
                Name=*) eval "$line" ;; # assuming $line is always bash-friendly Name="Value"
                *) echo "$Name;$line" ;;
        esac
done < <(egrep -o 'Name=".*"|<!\[CDATA\[.*?\]\]>' file.xml | sed -r 's/<!\[CDATA\["(.*)"\]\]>/\1/')

我稍微改变了你的命令以使用扩展正则表达式(这就是为什么它是“egrep”和“sed -r”)所以它更容易阅读。

我不喜欢我使用的那个eval,但是“export -n”在这种情况下做了一些奇怪的事情,并且为了避免使用eval,代码会变得不必要复杂。

如果你真的确定文本结构不会改变,那么在Bash中“解析”XML是可以的。一旦有人决定通过将XML全部折叠成一行来“优化”XML,你就会有点干杯。

修改

这是一个没有丑陋eval的脚本:

#!/bin/bash

Name="InvalidName"
while read line; do
        case "$line" in
                Name=*) export -n "$line" ;; # assuming $line is always bash-friendly Name=Value
                *) echo "$Name;$line" ;;
        esac
done < <(egrep -o 'Name=".*"|<!\[CDATA\[.*?\]\]>' file.xml | sed -r 's/<!\[CDATA\["(.*?)"\]\]>/\1/; s/Name="(.*)"/Name=\1/')