sed inline 无法修改 XML 样式的输入

sed inline 无法修改 XML 样式的输入

问题是我无法将完整的命令与 SED 放在同一行,我已经完成了它,但对于这些文件它不起作用:我的例子:

<file>Documents/time/text1</file> //2X slash + 2 words to remove !!
<file>Commun/text2</file>         //1X slash to remove + 1 words to remove
<file>Current/text3</file>        //1X slash to remove + 1 words to remove

为什么这段代码不能在线工作?

sed 's/Documents//g' | sed 's/time//g' | sed 's/Commun//g' | sed 's/Current//g' | sed 's/Current//g' | sed '/<file>/s|<file>/|<file>|' | sed '/<file>/s|<file>/|<file>|' tracklist.txt > newtracklist.txt

答案1

运行 OP 的当前脚本管道sed会导致输入文件 ( tracklist.txt) 的内容被打印到 stdout,然后管道挂起(即,没有其他输出,没有返回到命令提示符)。我是猜测这就是OP在陈述时所指的内容it does not work... ??

主要问题:输入文件 ( tracklist.txt) 需要作为第一个sed脚本的参数提供,而不是作为最后一个sed脚本的参数。

推荐:

# instead of this:

sed 's/Documents//g'               | ... |  sed '/<file>/s|<file>/|<file>|' tracklist.txt
                                                                            ^^^^^^^^^^^^^
# do this:

sed 's/Documents//g' tracklist.txt | ... |  sed '/<file>/s|<file>/|<file>|'
                     ^^^^^^^^^^^^^

运行 OPsed管道的更新版本会生成:

<file>text1</file>
<file>text2</file>
<file>text3</file>

虽然有更好的工具用于解析 HTML/XML,但如果 OP 必须使用,sed那么有多种方法可以以更有效的方式生成相同的结果。

一个想法需要一个sed脚本:

sed -E 's|(<file>).*/([^/]+</file>)|\1\2|' tracklist.txt

在哪里:

  • -E- 启用对扩展正则表达式的支持
  • (<file>)- (第一个捕获组)匹配字符串<file>
  • ([^/]+</file>)-(第二个捕获组)匹配所有后面不是/字符串的字符</file>
  • .*/- 两个捕获组之间的所有内容都以 a 结尾/
  • \1\2- 替换字符串由附加在一起的两个捕获组组成
  • 笔记:这适用于 OP 提供的特定输入;如果输入的格式与 OP 示例输入中显示的格式不同,则可能需要调整

对于OP的样本输入,这会生成:

<file>text1</file>
<file>text2</file>
<file>text3</file>

答案2

给定您的输入 XML 文件

添加r根节点:

<r>
    <file>Documents/time/text1</file>
    <file>Commun/text2</file>
    <file>Current/text3</file> 
</r>

代码:

xidel --xquery '
    <r>{
        for $x in //file
        return <file>{tokenize($x, "/")[last()]}</file>
    }</r>
' --output-format=xml --output-node-indent file.xml

产量:

<?xml version="1.0" encoding="UTF-8"?>
<r>
  <file>text1</file>
  <file>text2</file>
  <file>text3</file>
</r>

说明:

在这里,我没有使用错误的工具:sed,而是使用XPathand XQuery(前者是后者的子集)正确的XML解析器。

xidel是操作 HTML/XML 的瑞士军刀。

用法:

xidel ... file.xml > new_file.xml

如果你想编辑在飞行中:

xidel ... file.xml | sponge file.xml

spongeGNU more-utils

答案3

使用(以前称为 Perl_6)

~$ raku -MXML -e 'my  $xml = open-xml( $*ARGFILES.Str );
                  for $xml.elements( :RECURSE(0), :TAG{"file"} ) -> $E {
                      my $old = $E.contents[0];
                      my $new  = XML::Text.new( text => $old.text.match(/ <?after "/">  <-[/]>+  $/) );
                      $E.replace( $old, $new );
                  };  .say for $xml;'   file.xml

或者:

% raku -MXML -e 'my  $xml = open-xml( $*ARGFILES.Str );
                 for $xml.elements( :RECURSE(0), :TAG{"file"} ) -> $E {
                      my $old = $E.contents[0];
                      my $new  = XML::Text.new( text => $old.text.path.basename );
                      $E.replace( $old, $new );
                  };  .say for $xml;'   file.xml

Raku 是 Perl 家族中的一种编程语言,具有高级功能语法用于解析文本。除了 Raku/Rakudo 本身之外,社区成员还支持 Raku/Rakudo 生态系统中的模块。这些模块之一是(Raku-nativeXML模块。

与OP的其他问题类似,在带有XML-module的Raku中,您可以(例如)将替换限制为1)。顶层和 2)。仅在<file>TAG 内。这是通过将代码设置为在elements限制条件下进行迭代来完成的:RECURSE(0), :TAG{"file"}。仅供参考,TAG如果需要,您可以迭代所有深度的所有 s:只需设置:RECURSE(Inf)和删除:TAG命名参数,这会将:TAG限制设置为 False。

上面的第一个答案确定了适合替换的标签/级别。这样就确定了,每个元素的内部(即非TAG)contents[0]都被赋值给变量$old,它实际上是一个XML::Text对象。$old将对象提取.text到字符串中,然后match找到所需的对象。使用现已更正的键/值对XML::Text.new创建 ( )新的 ( ) 对象。从这里开始,-module 的例程完成了工作:。$newtext => 'value'XMLreplacereplace( $old, $new )

上面的第二个答案是第一个答案的巧妙转折。因为 OP 想要编辑路径名,所以IO::Path可以使用与 Raku 对象类相关的例程。 Raku 的.IO例程将文本理解为有效的路径名,并且 Raku 的.basename例程返回最终的文件名。这种方法有可能提高代码的可移植性,因为 Raku 具有在不同平台上使用正确 (/\) 路径分隔符的机制。

输入示例(感谢@GillesQuénot!):

<r>
    <file>Documents/time/text1</file>
    <file>Commun/text2</file>
    <file>Current/text3</file>
</r>

示例输出:

<?xml version="1.0"?><r>
    <file>text1</file>
    <file>text2</file>
    <file>text3</file>
</r>

https://github.com/raku-community-modules/XML
https://docs.raku.org/type/IO/Path
https://rakudo.org/
https://raku.org

相关内容