我知道以下命令将打印到特定模式的第一次出现,但不会包括后来的出现:
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///
命令末尾的编辑替换而获得额外的空行。因此,缓冲区会被交换,如果保持缓冲区不为空,则当前行将附加到该缓冲区中,后面是\n
ewline 字符 - 否则多余的行将来自该缓冲区。否则,缓冲区将被交换回来,并且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 .*? $$ /
.