用 xmlstarlet 迭代 xml 并输出父节点和子节点值
iterate over xml with xmlstarlet and output parent and child node values
我在多个 XML 文件中有这种格式:
<bad>
<objdesc>
<desc id="butwba10.1.wc.01" dbi="BUTWBA10.1.1.WC">
<physdesc>adfa;sdfkjad</physdesc>
<related objectid="bb435.1.comdes.02"/>
<related objectid="but614r.1.penc.01"/>
<related objectid="but611.1.wc.01"/>
<related objectid="but612.1.wd.01"/>
<related objectid="bb515.1.comb.12"/>
</desc>
<desc id="butwba10.1.wc.02" dbi="BUTWBA10.1.2.WC">
<physdesc>alkdjfa;sfjsdf</physdesc>
<related objectid="but621r.1.penc.01"/>
<related objectid="bb435.1.comdes.03"/>
</desc>
</objdesc>
</bad>
我想要这样的输出:
butwba10.1.wc.01 dbi="BUTWBA10.1.1.WC" related="bb435.1.comdes.02, but614r.1.penc.01, but611.1.wc.01, but612.1.wd.01, bb515.1.comb.12"
butwba10.1.wc.02 dbi="BUTWBA10.1.2.WC" related="but621r.1.penc.01, bb435.1.comdes.03"
我有一个 bash 脚本,它使用 xmlstarlet 遍历目录中的 xml 文件,但它会转储最后一个 desc 之后的所有 "related values" ID。它需要将每个 desc id 与每组 "related" 值相关联。并且需要包含每个id的dbi值。
#!/bin/bash
for x in *.xml
do
id=$(xml sel -t -v '//bad/objdesc/desc/@id' "$x")
arr=( $(xml sel -t -v '//bad/objdesc/desc/related/@objectid' "$x") )
cat<<EOF >> new_file
$id related="$(perl -e 'print join ",", @ARGV' "${arr[@]}")"
EOF
done
#!/bin/bash
for x in *.xml; do
count=$(xml sel -t -v 'count(//bad/objdesc/desc/@id)' "$x")
for ((i=1; i<=count; i++)); do
id=$(xml sel -t -v "//bad/objdesc/desc[$i]/@id" "$x")
arr=( $(xml sel -t -v "//bad/objdesc/desc[$i]/related/@objectid" "$x") )
cat<<EOF
$id related="$(perl -e 'print join ",", @ARGV' "${arr[@]}")"
EOF
done
done
=)
这似乎是 XSLT 的工作。但是,好的,shell 也可以处理这个...
你能为 dbi
完成剩下的工作吗?最好尝试理解这里涉及的内容,而不仅仅是 cut/paste.
同意 sputnick 的观点,XSLT 是正确的工具。尽管如此,使用 XML 标记解析器的 perl 答案。优点是它只需要处理一次文件而不是重复调用xmlstarlet:
#!perl
use strict;
use warnings;
use XML::Parser;
my (@related, @desc); # boo, global variables
sub start {
my ($x, $elem, %attrs) = @_;
if ($elem eq "desc") {
@desc = @attrs{'id', 'dbi'};
@related = ();
}
elsif ($elem eq "related") {
push @related, $attrs{objectid};
}
}
sub end {
my ($x, $elem) = @_;
if ($elem eq "desc") {
printf qq{%s dbi="%s" related="%s"\n}, @desc, join(', ', @related);
}
}
my $parser = XML::Parser->new( Handlers => {Start => \&start, End => \&end} );
$parser->parsefile($ARGV[0]);
在行动:
$ perl parse.pl file
butwba10.1.wc.01 dbi="BUTWBA10.1.1.WC" related="bb435.1.comdes.02, but614r.1.penc.01, but611.1.wc.01, but612.1.wd.01, bb515.1.comb.12"
butwba10.1.wc.02 dbi="BUTWBA10.1.2.WC" related="but621r.1.penc.01, bb435.1.comdes.03"
$ xml sel -t -m bad/objdesc/desc -v "concat(@id,' dbi=',@dbi,' ')" -m related -v @objectid -i "number(count(./preceding-sibling::related))+1<number(count(./../related))" -o ", " --else -n -b file.xml
butwba10.1.wc.01 dbi=BUTWBA10.1.1.WC bb435.1.comdes.02, but614r.1.penc.01, but611.1.wc.01, but612.1.wd.01, bb515.1.comb.12
butwba10.1.wc.02 dbi=BUTWBA10.1.2.WC but621r.1.penc.01, bb435.1.comdes.03
我在多个 XML 文件中有这种格式:
<bad>
<objdesc>
<desc id="butwba10.1.wc.01" dbi="BUTWBA10.1.1.WC">
<physdesc>adfa;sdfkjad</physdesc>
<related objectid="bb435.1.comdes.02"/>
<related objectid="but614r.1.penc.01"/>
<related objectid="but611.1.wc.01"/>
<related objectid="but612.1.wd.01"/>
<related objectid="bb515.1.comb.12"/>
</desc>
<desc id="butwba10.1.wc.02" dbi="BUTWBA10.1.2.WC">
<physdesc>alkdjfa;sfjsdf</physdesc>
<related objectid="but621r.1.penc.01"/>
<related objectid="bb435.1.comdes.03"/>
</desc>
</objdesc>
</bad>
我想要这样的输出:
butwba10.1.wc.01 dbi="BUTWBA10.1.1.WC" related="bb435.1.comdes.02, but614r.1.penc.01, but611.1.wc.01, but612.1.wd.01, bb515.1.comb.12"
butwba10.1.wc.02 dbi="BUTWBA10.1.2.WC" related="but621r.1.penc.01, bb435.1.comdes.03"
我有一个 bash 脚本,它使用 xmlstarlet 遍历目录中的 xml 文件,但它会转储最后一个 desc 之后的所有 "related values" ID。它需要将每个 desc id 与每组 "related" 值相关联。并且需要包含每个id的dbi值。
#!/bin/bash
for x in *.xml
do
id=$(xml sel -t -v '//bad/objdesc/desc/@id' "$x")
arr=( $(xml sel -t -v '//bad/objdesc/desc/related/@objectid' "$x") )
cat<<EOF >> new_file
$id related="$(perl -e 'print join ",", @ARGV' "${arr[@]}")"
EOF
done
#!/bin/bash
for x in *.xml; do
count=$(xml sel -t -v 'count(//bad/objdesc/desc/@id)' "$x")
for ((i=1; i<=count; i++)); do
id=$(xml sel -t -v "//bad/objdesc/desc[$i]/@id" "$x")
arr=( $(xml sel -t -v "//bad/objdesc/desc[$i]/related/@objectid" "$x") )
cat<<EOF
$id related="$(perl -e 'print join ",", @ARGV' "${arr[@]}")"
EOF
done
done
=)
这似乎是 XSLT 的工作。但是,好的,shell 也可以处理这个...
你能为 dbi
完成剩下的工作吗?最好尝试理解这里涉及的内容,而不仅仅是 cut/paste.
同意 sputnick 的观点,XSLT 是正确的工具。尽管如此,使用 XML 标记解析器的 perl 答案。优点是它只需要处理一次文件而不是重复调用xmlstarlet:
#!perl
use strict;
use warnings;
use XML::Parser;
my (@related, @desc); # boo, global variables
sub start {
my ($x, $elem, %attrs) = @_;
if ($elem eq "desc") {
@desc = @attrs{'id', 'dbi'};
@related = ();
}
elsif ($elem eq "related") {
push @related, $attrs{objectid};
}
}
sub end {
my ($x, $elem) = @_;
if ($elem eq "desc") {
printf qq{%s dbi="%s" related="%s"\n}, @desc, join(', ', @related);
}
}
my $parser = XML::Parser->new( Handlers => {Start => \&start, End => \&end} );
$parser->parsefile($ARGV[0]);
在行动:
$ perl parse.pl file
butwba10.1.wc.01 dbi="BUTWBA10.1.1.WC" related="bb435.1.comdes.02, but614r.1.penc.01, but611.1.wc.01, but612.1.wd.01, bb515.1.comb.12"
butwba10.1.wc.02 dbi="BUTWBA10.1.2.WC" related="but621r.1.penc.01, bb435.1.comdes.03"
$ xml sel -t -m bad/objdesc/desc -v "concat(@id,' dbi=',@dbi,' ')" -m related -v @objectid -i "number(count(./preceding-sibling::related))+1<number(count(./../related))" -o ", " --else -n -b file.xml
butwba10.1.wc.01 dbi=BUTWBA10.1.1.WC bb435.1.comdes.02, but614r.1.penc.01, but611.1.wc.01, but612.1.wd.01, bb515.1.comb.12
butwba10.1.wc.02 dbi=BUTWBA10.1.2.WC but621r.1.penc.01, bb435.1.comdes.03