我需要删除与正则表达式匹配的前 N 行,但保留最后一行。
28test
32test
something else
6test
something else
something else
4test
entirelysomethingelse
我想要这样的东西。
something else
something else
something else
4test
entirelysomethingelse
尝试使用sed
,似乎它只适用于一行和多个字符串。
我使用了正则表达式:^(.*test)$
答案1
仅保留最后一个匹配行的一个简单方法是反向打印输入,仅选择第一个匹配行,然后在管道中反向打印输出。假设tac
从GNU 核心实用程序可供您使用:
tac input_file | awk '!/test$/ || !seen++' | tac >output_file
就地编辑(按照您在评论)通常是通过将命令包装在脚本或函数中来获得的,该脚本或函数负责用处理后的输出覆盖作为参数给出的文件。
tmpdir=$(mktemp -d)
cp input_file "$tmpdir/file"
tac "$tmpdir/file" | awk '!/test$/ || !seen++' | tac >input_file
rm -r "$tmpdir"
如果您的 shell 支持该pipefail
选项(我可以使用setopt PIPE_FAIL
、 busybox ash、yash 成功测试 bash、ksh93、mksh、zsh),则可以使用set -e
and使这更安全set -o pipefail
:错误(包括管道中任何地方发生的错误)将使执行停止之前临时文件被删除。
在支持它的平台上,假设您不关心在出现问题时丢失数据,您还可以使用:
{ rm file; tac | awk '!/test$/ || !seen++' | tac >file; } <file
请注意,这将更改 的索引节点file
(就像许多常用工具提供的就地编辑选项一样)。
相反,如果您想删除第一个n匹配线,假设这里n= 2:
awk '!/test$/ || ++seen > 2' input_file >output_file
awk
在这种情况下, GNU 能够导入其他库(特别是自带的“就地”库) ,可以方便地进行就地编辑gawk
:
awk -i inplace '...' file
有关此内容的更多信息,请参阅这个另一个答案在 U&L 上。
答案2
至于选项 - “删除模式匹配的前 N 行”
在 上sed
,您可以在保留空间(附加缓冲区)中组织一个计数器。
sed -r '/test$/!b;x;s/$/-/;/-{4}/!{x;d};x' file
/test$/!b
- 如果没有模式匹配则无条件跳转到末尾。
x
- 交换模式和保留空间的内容。
s/$/-/
- 对于每个匹配,在计数器末尾添加一个字符,在我的例子中是一个连字符。
/-{4}/!{x;d}
- 如果计数器包含的字符少于 4 个字符,则删除模式空间中的行。