我们有一个 xml 文件(abc_lop.xml)
,我需要删除其中标签中存在的一行:
下面是一个 xml 文件,我已将其缩短,因为它很大。
<HELLO version="4.2" xmlns="http://www.bacd.org/HELLO-4_2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bacd.org/HELLO-4_2 http://www.bacd.org/v4-2/hello-4-2.xsd">
<!-- some data here -->
</HELLO>
正如你所看到的,我在上面的标签xsi:schemaLocation="http://www.bacd.org/HELLO-4_2 http://www.bacd.org/v4-2/hello-4-2.xsd"
中有这一行。HELLO
我需要删除这条线并保留其他东西。
截至目前,我正在向上面的 xml 文件添加一些页眉和页脚,如下所示在我的 shell 脚本中,然后将其存储在文件变量中: Here $word
is abc
.
file=$(printf '%s\n%s\n%s' "$header" "$(cat "$path/${word}_lop.xml")" "$footer")
现在我想确保文件变量应该具有 xml 文件数据,但也从HELLO
标记中删除该行。
我稍后会将此$file
变量用于其他目的,因此我想确保$file
也应删除页眉、页脚和该行。具有 key=value 对的行只会出现一次。
答案1
请不要使用正则表达式来修改 XML。 XML 规范允许一些与基于正则表达式的解析无法很好地配合的事情。
这是一个非常糟糕的主意,因为你创建了脆弱的代码。有一天,您正在使用的源 XML 可能会更改为其他完全有效的内容(就 XML 规范而言),并且您的下游修改脚本将会中断。
这正是让系统管理员和维护程序员非常悲伤的事情。
请使用 XML 解析器。xmlstarlet
是一种选择。两者perl
也python
都有解析选项。这两件事都处理 XML 中的奇怪情况(例如换行、漂亮的打印等)并确保下游 XML 有效 - 输出有效的 XML 很重要,因为无效的 XML应该成为致命的状况。
具体来说 - 从HELLO
元素中删除一个属性:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $twig = XML::Twig -> new ( 'pretty_print' => 'indented_a' ) -> parse ( \*DATA );
foreach my $hello ( $twig -> findnodes ('//HELLO') ) {
$hello -> del_att('xmlns:xsi');
}
$twig -> print;
__DATA__
<HELLO version="4.2" xmlns="http://www.bacd.org/HELLO-4_2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bacd.org/HELLO-4_2 http://www.bacd.org/v4-2/hello-4-2.xsd">
<!-- some data here -->
</HELLO>
注意 - 我已经“漂亮地打印”了结果:
<HELLO
version="4.2"
xmlns="http://www.bacd.org/HELLO-4_2"
xsi:schemaLocation="http://www.bacd.org/HELLO-4_2 http://www.bacd.org/v4-2/hello-4-2.xsd">
<!-- some data here -->
</HELLO>
给我们一个例子来说明为什么基于正则表达式的解析是一个坏主意 - 因为那是有效的 XML。
也是如此:
<HELLO version="4.2" xmlns="http://www.bacd.org/HELLO-4_2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bacd.org/HELLO-4_2 http://www.bacd.org/v4-2/hello-4-2.xsd">
<!-- some data here -->
</HELLO>
和:
<HELLO
version="4.2"
xmlns="http://www.bacd.org/HELLO-4_2"
xsi:schemaLocation="http://www.bacd.org/HELLO-4_2 http://www.bacd.org/v4-2/hello-4-2.xsd"
>
<!-- some data here -->
</HELLO>
和:
<HELLO version="4.2" xmlns="http://www.bacd.org/HELLO-4_2" xsi:schemaLocation="http://www.bacd.org/HELLO-4_2 http://www.bacd.org/v4-2/hello-4-2.xsd"><!-- some data here --></HELLO>
但 XML 解析器既简单又好用。
要将代码简化为像 sed 这样的单行代码:
perl -0777 -MXML::Twig -e 'XML::Twig -> new ( pretty_print => "indented_a", twig_handlers => { "HELLO" => sub { $_ -> del_att("xmlns:xsi") }} ) -> parse ( <> ) -> print;'
如果通过 STDIN 或通过指定文件名提供数据,应该可以工作。
答案2
要删除该xsi:schemaLocation
条目,并保持文件的其余部分不变:
$ sed 's/xsi:schemaLocation="[^"]*"//' "$path/${word}_lop.xml"
<HELLO version="4.2" xmlns="http://www.bacd.org/HELLO-4_2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
<!-- some data here -->
</HELLO>
s/xsi:schemaLocation="[^"]*"//
是一个替代命令。它将任何xsi:schemaLocation="[^"]*"
与正则表达式匹配的内容替换为空。
要将其与您的脚本结合起来:
file=$(printf '%s\n%s\n%s' "$header" "$(sed 's/xsi:schemaLocation="[^"]*"//' "$path/${word}_lop.xml")" "$footer")