sed :有一个范围以最后一次出现的模式结束(贪婪范围)

sed :有一个范围以最后一次出现的模式结束(贪婪范围)

获取以下文件:

$ cat f1
stu vwx yza
uvw xyz abc
abc def ghi
def ghi jkl
ghi jkl mno
jkl mno pqr
mno pqr stu
pqr stu vwx
stu vwx yza

打印从第一行开始包含到abc的所有行第一个包含mnoGNU sed

$ sed -n '/abc/,/mno/p' f1
uvw xyz abc
abc def ghi
def ghi jkl
ghi jkl mno

我怎样才能打印所有行直到最后一个包含mno,例如我怎样才能得到以下结果:

uvw xyz abc
abc def ghi
def ghi jkl
ghi jkl mno
jkl mno pqr
mno pqr stu

换句话说,有没有办法让GNUsed的范围选择变得贪婪?

更新

在我的设置中:

  • 如果mno丢失,它应该打印出所有内容,直到文件末尾。
  • mno不能发生在第一个之前abc
  • 总是至少有一个abc,abcmno永远不会在同一行

编辑 我只是stu vwx yza在开头添加了一条虚拟行,以便文件不会以包含的行开头abc(以避免从第一行开始的解决方案 - 它们应该从abc其中包含的第一行开始)

答案1

sed '/abc/,$!d;0,/mno/b;:1;/mno/b;$d;N;b1' file

工作算法:
使用两个地址范围。
第一个/abc/,$!d;删除第一个模式匹配之前的所有内容。
第二个0,/mno/b;直到与模式匹配为止/mno/,将每个行缓冲区(模式空间)发送到绕过剩余脚本的输出,从而防止在文件中找不到模式时删除。
脚本的其余部分:1;/mno/b;$d;N;b1循环工作。在编辑器缓冲区中,行会被追加,直到出现模式匹配为止。如果/mno/遇到模式,整个缓冲区将被发送到输出,绕过脚本的其余部分。如果没有匹配,则删除缓冲区的最后一行。

答案2

awk如果有的话你可以使用。您可以标记模式开始和模式停止的行,并在文件的一次传递中打印这些行(涉及存储从第一行开始abc到缓冲区中最后一行的行)

awk '/abc/ && !start {
  start = NR
}
/mno/ {
  stop = NR
}
start { line[NR] = $0 }
END {
  if ( !stop ) {
    stop = NR
  }
  for ( s = start; s <= stop; s++ )
    print line[s]
}' file

请注意,当起始图案不存在时,这将不起作用,仅打印一系列空白行。

答案3

另一种awk缓冲较少的解决方案:

awk '!f&&/abc/{f=1} f==1; f==2{buf=buf $0 ORS} f&&/mno/{f=2; printf "%s",buf; buf=""}' input.txt
  • 这将打印从第一次出现abc(将标志设置f为 1)开始直到(包括第一次出现)的所有内容mnof==1规则块之外的语句指示只要awk设置为 就打印当前f1
  • 然后,每次出现 后mnof现在值为 2)所有行的内容都存储在缓冲区 中buf,该缓冲区在下一次出现 时被打印并清除mno。确保我们正确应对第一个mno发生的情况首先abc,我们要求f在应用该逻辑之前至少将其设置为 1。

因此,它最多会存储两次出现之间的文本mno,或者最后一次出现mno和文件结束之间的文本(只是后一部分永远不会被打印)。

如果你想用内存效率来交换速度,下面的两遍方法将根本不依赖缓冲:

awk 'FNR==NR{if (/abc/&&!start) {start=FNR} else if (/mno/) {end=FNR}; next} FNR>=start&&(!end||FNR<=end)' input.txt input.txt

这将处理文件两次(因此将其指定为参数两次)。

  • 第一次,当FNR每个文件的行计数器等于NR全局行计数器时,我们查找第一次出现的abc和 最后一次出现的mno,并将它们的行号分别存储在start和中end
  • 在第二遍中,只要计数器FNR位于(并包括)startend之间end(或者只是它大于/等于未设置的start情况),我们就打印行。end

答案4

您可以收集所有行,从abc保留空间中的行开始,然后使用贪婪性质.*删除最后一个之后的所有内容mno

sed '/abc/,$!d;H;$!d;x;s/\n//;s/\(.*mno[^\n]*\).*/\1/'
  • /abc/,$!dd删除第一abc行之前的所有内容(或整个文件,如果abc根本没有行)
  • H;$!d是在保留空间中收集整个文件的经典模式(请注意,这对于非常大的文件可能是一个问题)
  • 我们x更改缓冲区而不是使用g以避免复制大缓冲区
  • s/\n//删除开头错误的换行符,该换行符是通过附加到空的保留空间而产生的
  • s/\(.*mno[^\n]*\n\).*/\1/删除之后的所有内容最后的 mno行(或者根据要求打印整个剩余文件,如果没有mno行)。请注意,这[^\n]不是 POSIX,并且仅适用于某些版本,例如 GNU sed

相关内容