如何转义 sed“子命令”中的特殊字符?

如何转义 sed“子命令”中的特殊字符?

我正在使用xmllint --shell一个大型 XML 文件,并使用该write命令写出要在测试中使用的 XML 片段。写出的代码片段需要原始 XML 文件中的几行(声明、命名空间和根节点)。我希望能够将这些行添加到文件中,而无需手动复制这些行。相反,我想使用 sed 为我附加这些行,这样我就可以编写一个函数来自动执行这项非常繁琐的任务。为了说明这一点,这是我想要完成的任务的一个示例。

源 XML (source.xml):

<?xml version="1.0" encoding="UTF-8"?>
<foo:root xmlns:foo="TheFooNameSpaceIsImportant">
    <foo:Entry>
        <foo:SomeNode>Foo1</foo:SomeNode>
        <foo:AnotherNode>Bar1</foo:AnotherNode>
    </foo:Entry>
    <foo:Entry>
        <foo:SomeNode>Foo2</foo:SomeNode>
        <foo:AnotherNode>Bar2</foo:AnotherNode>
    </foo:Entry>
    <foo:Entry>
        <foo:SomeNode>Foo3</foo:SomeNode>
        <foo:AnotherNode>Bar3</foo:AnotherNode>
    </foo:Entry>
    <!-- tens of thousands of others -->
    <foo:Entry>
        <foo:SomeNode>Foo20432</foo:SomeNode>
        <foo:AnotherNode>Bar20432</foo:AnotherNode>
    </foo:Entry>

</foo:root>

保存的 XML 片段 (sample.xml):

<foo:Entry>
    <foo:SomeNode>Foo</foo:SomeNode>
    <foo:AnotherNode>Bar</foo:AnotherNode>
</foo:Entry>

所以我需要用 source.xml 的顶部两行和底部一行来包裹它。但由于角色的原因,以下失败<

$ sed -i 1i"`head -n 2 source.xml`" sample.xml
sed: -e expression #1, char 43: unknown command: `<'

当从这样的子命令提供该字符时,有没有办法转义该字符?

答案1

命令sed“i”需要后跟文本,如给定上面提供的命令时\BSD 的输出中所解释的那样。sed

然而,它只期望文本行。要插入更多内容,您需要在第一行末尾添加反斜杠:

sed "1i\\
$(head -n 2 source.xml | sed 's/$/\\/')
" sample.xml

这(嵌套sed调用)有点荒谬。正如我所写的别处,就地脚本文件编辑选择的工具不是sed,而是ex

ex -sc '1,2ya | n! | 0pu | x' source.xml sample.xml

-s标志ex以静默模式启动,用于批处理。 -c指定要运行的命令。

1,2ya猛拉(即复制)第一个文件的前两行,source.xml.

|是命令分隔符。

n!转到下一个文件,放弃对当前文件所做的任何更改。 (我们在这种情况下没有做任何事情,所以n也可以。)

0pu“放置”(即粘贴)我们之前复制的行,将它们放置在行“0”之后(即,将它们粘贴到第一行上方)。

x退出,保存对当前文件所做的更改。

与 POSIX 中未指定的命令不同sed -i(并且在 BSD 上不起作用,BSDsed需要给定备份文件扩展名,-i即使为空),上面的ex命令完全是符合 POSIX 标准

答案2

当您插入/追加多行时,您必须转义行尾,以便sed知道何时停止插入/追加。在你的情况下你可以运行

head -n 2 source.xml | sed '1i\
1i\\
s/\\/&&/g
$!s/$/\\/' | sed -f - sample.xml

第一个sed处理输入(1i\在这两行之前添加命令,转义任何反斜杠以及行尾(如果不是最后一行)并将其作为脚本传递sed给第二个命令。如果您想就地编辑,请添加-i到第二个。sed

答案3

不要sed与 一起使用XML。 XML 是一种上下文数据结构,而正则表达式根本无法很好地支持。https://stackoverflow.com/questions/1732348/regex-match-open-tags- except-xhtml-self-contained-tags

使用解析器。perl效果XML::Twig很好:

#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
use XML::Twig;

my $xml_to_insert = XML::Twig -> parse ( '<foo:Entry>
    <foo:SomeNode>Foo</foo:SomeNode>
    <foo:AnotherNode>Bar</foo:AnotherNode>
</foo:Entry>') -> root -> copy;

my $xml = XML::Twig -> parse ( \*DATA ); 

$xml_to_insert -> paste ( 'first_child', $xml -> root );
$xml -> set_pretty_print ( 'indented_a');
$xml -> print;


__DATA__
<?xml version="1.0" encoding="UTF-8"?>
<foo:root xmlns:foo="TheFooNameSpaceIsImportant">
    <foo:Entry>
        <foo:SomeNode>Foo1</foo:SomeNode>
        <foo:AnotherNode>Bar1</foo:AnotherNode>
    </foo:Entry>
    <foo:Entry>
        <foo:SomeNode>Foo2</foo:SomeNode>
        <foo:AnotherNode>Bar2</foo:AnotherNode>
    </foo:Entry>
    <foo:Entry>
        <foo:SomeNode>Foo3</foo:SomeNode>
        <foo:AnotherNode>Bar3</foo:AnotherNode>
    </foo:Entry>
    <!-- tens of thousands of others -->
    <foo:Entry>
        <foo:SomeNode>Foo20432</foo:SomeNode>
        <foo:AnotherNode>Bar20432</foo:AnotherNode>
    </foo:Entry>

</foo:root>

输出:

<?xml version="1.0" encoding="UTF-8"?>
<foo:root xmlns:foo="TheFooNameSpaceIsImportant">
  <foo:Entry>
    <foo:SomeNode>Foo</foo:SomeNode>
    <foo:AnotherNode>Bar</foo:AnotherNode>
  </foo:Entry>
  <foo:Entry>
    <foo:SomeNode>Foo1</foo:SomeNode>
    <foo:AnotherNode>Bar1</foo:AnotherNode>
  </foo:Entry>
  <foo:Entry>
    <foo:SomeNode>Foo2</foo:SomeNode>
    <foo:AnotherNode>Bar2</foo:AnotherNode>
  </foo:Entry>
  <foo:Entry>
    <foo:SomeNode>Foo3</foo:SomeNode>
    <foo:AnotherNode>Bar3</foo:AnotherNode>
  </foo:Entry>
  <!-- tens of thousands of others -->
  <foo:Entry>
    <foo:SomeNode>Foo20432</foo:SomeNode>
    <foo:AnotherNode>Bar20432</foo:AnotherNode>
  </foo:Entry>
</foo:root>

为了便于说明,这更长、更详细 - 但它本质上是获取您的代码片段,并将其复制粘贴到您的结构中。又好又简单。

XML::Twig还支持“parsefile_inplace”,它允许您执行与sed -i.所以你的例子看起来更像是:

my $xml_to_insert = XML::Twig -> parsefile ( 'source.xml' ) -> root -> copy;

XML::Twig -> new ( pretty_print => 'indented_a',
                   twig_handlers => { 
                       'foo:root' => sub {  
                            $xml_to_insert -> paste ( 'first_child', $_ ) 
                        } }) -> parsefile_inplace ('sample.xml'); 

或者如果这看起来有点太复杂了:

sub insert_source {
    my ( $twig, $branch ) = @_;  
    my $xml_to_insert = XML::Twig -> parsefile ( 'source.xml' ) -> root -> copy; 
    $xml_to_insert -> paste ( 'first_child', $branch ); 
}

my $xml = XML::Twig -> new ( twig_handlers => { 'foo:root' => \&insert_source } );
   $xml -> parsefile_inplace ( 'sample.xml'); 

相关内容