【问题标题】:How to find the 2nd occurence of a string and then delete a line 2 lines below that occurence如何找到字符串的第二次出现,然后删除该出现下 2 行的行
【发布时间】:2019-06-21 06:02:24
【问题描述】:

我有一个 xml 文件,我想找到第二次出现的字符串,然后从字符串中删除第 2 行。以下是它的外观示例:

<application>
    <app>
       <Name>Tester</Name>
       <Class>tester.update</Class>
       <const>
            <argument>READ_ONLY</argument>
       </const>
       <role>sysad</role>
</application>

<application>
    <app>
       <Name>Tester</Name>
       <Class>tester.update</Class>
       <const>
             <argument>READ_ONLY</argument>
       </const>
       <role>tester</role>
</application>

这是我想要的样子:

<application>
    <app>
       <Name>Tester</Name>
       <Class>tester.update</Class>
       <const>
            <argument>READ_ONLY</argument>
       </const>
       <role>sysad</role>
</application>
.
.
.
<application>
    <app>
       <Name>Tester</Name>
       <Class>tester.update</Class>
       <const>
       </const>
       <role>tester</role>
</application>

参数标签应该从第二个实例中完全删除

我正在搜索 tester.update,然后尝试删除第二次出现 tester.update 时向下 2 行的行

我尝试过类似的操作:

sed -i 'tester.update/{p;N;d}' file.txt 

但这会从tester.update. 的两个实例中删除第二行

提前感谢您的帮助

【问题讨论】:

标签: perl awk sed


【解决方案1】:

使用 XML 解析器的 Perl 解决方案会简单得多。这是Mojo::DOM 的样子,它使用CSS rules 查找标签:

use strict;
use warnings;
use Mojo::DOM;
use open ':std', ':encoding(UTF-8)';

my $xml = do { local $/; <> };
my $dom = Mojo::DOM->new->xml(1)->parse($xml);
$dom->at('application:nth-of-type(2) > app > const')->content('');
print $dom->to_string;

结果:

<application>
    <app>
       <Name>Tester</Name>
       <Class>tester.update</Class>
       <const>
            <argument>READ_ONLY</argument>
       </const>
       <role>sysad</role>
</app></application>

<application>
    <app>
       <Name>Tester</Name>
       <Class>tester.update</Class>
       <const />
       <role>tester</role>
</app></application>

(它甚至修复了 &lt;app&gt; 缺少的关闭标签)

【讨论】:

  • 感谢您的回复。缺少的应用程序标签是我的胖手指错字。看起来代码还删除了会破坏 XML 的 打开标记。
  • @chadopp &lt;const /&gt; 是有效的 XML,本质上与 &lt;const&gt;&lt;/const&gt; 的含义相同。
  • 如果您真的想将标签分开,您可以将内容替换为空格字符。 -&gt;content("\n")-&gt;content(' ')
  • 或者你可以更手术删除&lt;argument&gt;标签,留下周围的空白:-&gt;at('argument')-&gt;remove;
【解决方案2】:

假设您没有 XML 解析器:

sed 用于在各个行上执行 s/old/new,仅此而已。对于其他任何你应该使用 awk 的东西,例如在每个 UNIX 机器上的任何 shell 中使用任何 awk,您都可以清楚而简单地执行以下操作:

$ awk '/tester.update/{if (++cnt==2) skip=NR+2} NR!=skip' file
<application>
    <app>
       <Name>Tester</Name>
       <Class>tester.update</Class>
       <const>
            <argument>READ_ONLY</argument>
       </const>
       <role>sysad</role>
</application>

<application>
    <app>
       <Name>Tester</Name>
       <Class>tester.update</Class>
       <const>
       </const>
       <role>tester</role>
</application>

想要找到tester.update 的第 127 次出现而不是第 2 次?只需将cnt==2 更改为cnt==127。想要在找到之后删除第 93 行而不是第 2 行?只需将skip=NR+2 更改为skip=NR+93。尝试对 sed 解决方案进行类似(或其他任何事情!)的微不足道的更改。

为了简洁而喜欢 sed?您也可以在 awk 中为了简洁而牺牲清晰度:

$ awk '/tester.update/&&++c==2{s=NR+2}NR!=s' file
<application>
    <app>
       <Name>Tester</Name>
       <Class>tester.update</Class>
       <const>
            <argument>READ_ONLY</argument>
       </const>
       <role>sysad</role>
</application>

<application>
    <app>
       <Name>Tester</Name>
       <Class>tester.update</Class>
       <const>
       </const>
       <role>tester</role>
</application>

但重要的是您不必,最后,如果您喜欢使用 GNU sed 使用 -i 进行就地编辑,GNU awk 与 -i inplace 相同。

【讨论】:

  • Ed,我真的很喜欢你的解决方案。令我惊讶的是,有多少种不同的方法可以达到一个结果。我正在查看我的 gnu awk,但没有看到 -i 选项。我有版本 3.1.8。可能版本太旧了。
  • @chadopp 是的,那是 非常 旧的,例如大约 5 年过时。当前版本是 5.0.1,具有大量强大的功能和 3.1.8 中缺少的一些错误修复。在任何情况下,您都可以使用awk 'script' file &gt; tmp &amp;&amp; mv tmp file - 这正是sed -iperl -iawk -i inplace 和所有其他“就地编辑”工具(ed 除外,它使用与输入大小相同的内部缓冲区文件)无论如何都在后台执行。
  • 是的,不幸的是,这些系统处于我目前可以更改的基线上。非常感谢您的意见和帮助
  • 是的,这绝对值得考虑。 awk 解决方案更容易理解。
  • 我认为 awk 解决方案更适合我的工作,谢谢。
【解决方案3】:

这可能对你有用(GNU sed):

sed -Ei '/tester.update/{x;s/^/x/;/^x{2}$/{;x;n;n;d};x}' file

每次看到字符串tester.update,在保持空间中增加一个计数器。如果该计数器包含 2,则换回当前行,打印它和下一行并删除下一行。

【讨论】:

  • 这正是我想要的。感谢您花时间写这篇文章。
猜你喜欢
  • 2017-07-07
  • 1970-01-01
  • 1970-01-01
  • 2018-02-05
  • 1970-01-01
  • 2023-01-18
  • 1970-01-01
  • 2015-09-19
  • 1970-01-01
相关资源
最近更新 更多