0

我正在尝试从这个 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......也可能是Integer, Title, Boolean, 等等......

我试过这个

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

这给了我

Name="descResist"
resist_type_chimney
90_min.

如何在下一行前面加上 Name 字符串的值?

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

它变得有点复杂。

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

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

4

2 回答 2

2

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

内容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.
于 2013-07-02T11:45:10.803 回答
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,您就有点吃不消了。

编辑

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

#!/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/')
于 2013-07-02T13:33:08.480 回答