【问题标题】:Should I parse this XML with BASH?我应该用 BASH 解析这个 XML 吗?
【发布时间】:2023-04-08 17:16:01
【问题描述】:

我正在尝试从这个 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 等...

我试过了

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

这给了我

Name="descResist"
resist_type_chimney
90_min.

如何在下一行添加名称字符串的值?

点赞

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

这有点复杂。

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

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

【问题讨论】:

标签: xml bash sed awk cygwin


【解决方案1】:

试试这个:

#!/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/')

【讨论】:

  • 很好,这看起来比我的解决方案好得多。但我不太喜欢那个 eval(和你一样)。
【解决方案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.

【讨论】:

  • 我不能在 cygwin 上安装 perl 并且真的在寻找 bash/sed/awk 解决方案。但是谢谢
  • 看看perldoc.perl.org/perlcygwin.html。不是直截了当,但可能会有所帮助。
  • 我无法安装任何东西,这就是问题所在:)
猜你喜欢
  • 2013-06-06
  • 2017-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-01
  • 1970-01-01
相关资源
最近更新 更多