如何有效地替换大文件中最后出现的模式

时间:2011-12-21 17:26:20

标签: windows bash text command-line sed

给定一个包含以下内容的文件:

<root>
<a></a>
<b></b>
</root>

命令应输出:

<root>
<a></a>
<b></b>

我尝试使用GNU Win32的{​​{1}}端口尝试的内容:

删除最后两行。

这很快,但它假设sed是倒数第二行,如果不是则会导致错误。

</root>

用空字符串替换所有出现的sed -e '$d' test.xml | sed -e '$d'

这样可行,但比第一个解决方案慢,如果有嵌套的</root>元素(不太可能),它将会中断。

<root>

我正在处理的文件可能很大,因此效率非常重要。

有没有办法将sed替换限制在文件的最后一次出现?或者是否有其他更快的实用程序?

5 个答案:

答案 0 :(得分:2)

使用Perl和File :: Backwards应该非常快(相对,我知道,但仍然......)。 Perlfaq5有a topic向后浏览文件并删除行。您可以使用此主题的代码作为起点来检查您的模式。

答案 1 :(得分:1)

使用sed

sed -e ':a;N;$!ba;s|\(.*\)</root>\n\(.*\)|\1\2|'

答案 2 :(得分:1)

如何使用awk

AWK:

awk '/^<\/root>$/{next}/<\/root>/{sub(/<\/root>/,"");print;next}1' filename

第一个 /pattern/{action}语句会查找仅 </root>的行。它模式找到它,动作忽略它。

第二个 /pattern/{action}语句会在该行中查找包含</root> 任何地方的行。如果模式找到它,sub function将其替换为空,并打印其余部分。

第三次 操作1对于其中没有模式</root>的所有行都是如此。如果找到它,它会打印出来。

我做了一个快速测试,这就是结果 -

<强>测试

[jaypal:~/Temp] cat tmp
<root>
<a></a>
<b></b>
</root>
<root>
<a></a>
<b></b>
</root><root>
<a></a>
<b></b></root>
[jaypal:~/Temp] awk '/^<\/root>$/{next}/<\/root>/{sub(/<\/root>/,"");print;next}1' tmp
<root>
<a></a>
<b></b>
<root>
<a></a>
<b></b>
<root>
<a></a>
<b></b>

SED:

这也应该有用。虽然它会删除所有</root>而不仅仅是最后一次出现。

sed '/<\/root>/,$s///' filename

答案 3 :(得分:1)

这可能对您有用:

 sed '/<\/root>/,/<root>/{/<\/root>/{h;d};H;//{x;p};${x;s/[^\n]*\n//p};d}' file

这假定每个<root>标记与结束</root>标记匹配,并且这些标记出现在单独的行上(根据示例)。

说明:

  1. 关注结束</root>代码与开场<root>代码或文件结尾之间的界限。
  2. 如果是关闭</root>标记,请将其保存在保留空间(HS)中,然后将其删除并开始新的循环。
  3. 对于焦点内的所有其他行(请参阅第1点),将它们附加到HS。
  4. 如果是,并打开<root>标记,请交换到HS并打印出其内容。
  5. 如果它是文件结尾,即在</root>标记和文件的最后一行之间,则切换到HS,删除第一行,即结束</root>标记并打印剩余部分
  6. 对于焦点范围内的所有行,删除并开始新的循环。
  7. 两次通过的替代解决方案:

    sed -n '/<\/root>/=' file | sed -n '$s/$/d/p' | sed -f - file
    

    说明:

    1. 打印出结束</root>代码
    2. 的行号
    3. 从最后匹配的行号生成sed delete命令。
    4. 将命令传递给读取源文件的sed实例。

答案 4 :(得分:0)

使用时间功能查看哪一个是有效的。 sed应该是有效的。

$time command

在我看来,没有比grep更快的东西。尝试使用awk index()来查看它是否更快。