使用 sed 从第一行打印到包含最后一次出现的模式的行?

使用 sed 从第一行打印到包含最后一次出现的模式的行?

我知道以下命令将打印到特定模式的第一次出现,但不会包括后来的出现:

sed -n '1,/<pattern>/p' <file>

sed '/<pattern>/q' <file>

例如,假设我有一个包含以下几行的文件:

this is a cow   
this is a goat  
this is a some fish  
this is a fishie  
this is a fish  
this is a lion  
this is a cat

现在输出:

$ sed '/fish/q' file  

this is a cow  
this is a goat  
this is a some fish 

$ sed -n '1,/fish/p' file  

this is a cow  
this is a goat  
this is a some fish 

我希望输出从第一行开始直到包含最后一次出现的行即我想要的输出是:

this is a cow   
this is a goat  
this is a some fish  
this is a fishie  
this is a fish 

如何使用来实现sed

答案1

尝试这个:

$ tac infile | sed -n '/fish/,$p' |tac

一般情况下,如果您在 sed 命令下运行,您将获得从第一个匹配模式到输入文件末尾的所有行。

$ sed -n '/fish/,$p' file

this is a some fish
this is a fishie
this is a fish
this is a lion
this is a cat

所以我的解决方案是:如果我们tac在输入文件上运行命令,您最后一个匹配的模式将更改为第一个模式。查看结果tac infile

$ tac infile

this is a cat
this is a lion
this is a fish
this is a fishie
this is a some fish
this is a goat
this is a cow

tac命令与命令相同cat,但tac打印文件的顺序相反。

现在,如果我们运行第一个 sed 命令,您将获得所有行首先匹配模式到输入文件末尾。喜欢:

$ tac infile | sed -n '/fish/,$p'
this is a fish
this is a fishie
this is a some fish
this is a goat
this is a cow

好的,完成了。我们只需要tac再次运行命令即可将各行恢复到原始顺序:

$ tac infile | sed -n '/fish/,$p' |tac
this is a cow
this is a goat
this is a some fish
this is a fishie
this is a fish

完毕!

答案2

sed这相当简单 - 只需要在 的两个缓冲区之间进行一些协调即可。例如:

sed -n 'H;/fish/!d;$!n;x;p;G
' <<\INFILE
this is a cow
this is a goat
this is a some fish
this is a fishie
this is a fish
this is a lion
this is a cat
INFILE

该命令将每一行附加到H插入的 ewline 字符后面的旧空间\n。任何不!匹配的行/fish/都会立即d从输出中删除。这让我们只剩/fish/下线条了。因此该行被n输入的 ext 行覆盖。然后图案和h旧的空间被交换——H毕竟我们只是旧了这条线。现在模式空间是H旧空间,反之亦然。因此,我们只p打印上次/fish/行匹配时保存的内容。

这只能到达最后一次出现的匹配,因为它只p在找到匹配时才打印 - 并且它会在中间存储中间行。尽管如此,它只在比赛之间存储尽可能少的内容 - 每次缓冲区更改时x都会刷新它们。这是你的输出:

this is a cow   
this is a goat  
this is a some fish  
this is a fishie  
this is a fish

非常感谢唐·克里斯蒂让我知道我有时会跳过一条鱼。现在,它确保每次刷新时都将缓冲区推到两端 - 并且每次在删除它之前都会覆盖当前模式空间 - 以防万一。它适用于第一条线或最后一条线上的鱼,以及据我所知介于两者之间的任何一条线。

我正在做的另一件事是n在最后一行拉动分机线 - 这是一个很大的sed禁忌。再次感谢唐在这方面为我提供的帮助。

一个更彻底的例子:

sed 'x;/./G;//!x;/fish/p;//s/.*//;x;d'

我希望这能更好地解决其他问题。每个周期h都会更改模式/旧空格x- 这是为了避免由于s///命令末尾的编辑替换而获得额外的空行。因此,缓冲区会被交换,如果保持缓冲区不为空,则当前行将附加到该缓冲区中,后面是\newline 字符 - 否则多余的行将来自该缓冲区。否则,缓冲区将被交换回来,并且h旧空间在当前周期中保持为空。据我所知,此命令保留所有空白行和其他所有内容 - 但只是在最后一个匹配处停止打印。

我遇到的一些困难通常与h旧空间有关 - 有效使用它的唯一方法是低于 - 落后 - 线路周期,以便将旧线路与旧线路进行比较。我通常的偏好是循环N;P;D。你可能会使用类似的东西来做这件事......

sed -ne :n -e '/fish/!N;//p;//!bn'

不断地将输入的 ext 行sed附加N到模式空间,并b返回到:n标签以重试,如果与迄今为止建立的任何线路都不匹配。它仅p打印行 - 或行序列 -fish在行周期结束时转储内容并使用新缓冲区重新开始之前匹配。

我故意不在这里测试最后一行 - 如果最后一行匹配,它将被打印,否则 - 即使-n对于 GNU 也是如此sed- 循环将仅结束文件的所有最后一行或没有。

答案3

您还可以使用 awk,它可能比 Sed 短:

awk ' /^fish/ { print $0 }' filename.txt

有些人可能会这样写:

awk ' /^fish/ { print $1 $2 $3 $4 $5 }' filename.txt

代表$n一列。快捷方式$0代表整行。

答案4

使用 Raku(以前称为 Perl_6)

raku -e '$/.put if m/^ .* fish/ for lines.join("\n");'

输入示例:

this is a cow   
this is a goat  
this is a some fish  
this is a fishie  
this is a fish  
this is a lion  
this is a cat

示例输出:

this is a cow   
this is a goat  
this is a some fish  
this is a fishie  
this is a fish

请注意,上面的正则表达式将在单词上终止,fish只要fish是该行的最后一个单词就可以正常工作。然而,如果OP想要捕获整个最后一行(例如,fisherman返回整个单词),然后将上面的正则表达式匹配器更改为m/^ .* fish .*? $$ /.

https://raku.org

相关内容