使用 sed 或 awk 打印匹配的模式和前一行,直到换行符或空格

使用 sed 或 awk 打印匹配的模式和前一行,直到换行符或空格

我想打印“tmp 和之前的行,直到换行符”,我是 Unix 的新手。

例如:输入

AAA
CBH
VFh

GFD
DFC
VGF
HGD
TMP
JHG

TFD
PI1
98A
TMP
TMP
765
UBS

我想要的输出应该是这样的

GFD
DFC
VGF
HGD
TMP

TFD
PI1
98A
TMP
TMP

答案1

使用awk

$ awk -v RS='' -v ORS='\n\n' 'match($0,".*TMP") { print substr($0,1,RLENGTH) }' file
GFD
DFC
VGF
HGD
TMP

TFD
PI1
98A
TMP
TMP

这将由至少一个空行分隔的一组行视为一条记录。如果记录与正则表达式匹配.*TMP,则删除匹配后面的位并打印记录的其余部分。

TMP通过将正则表达式.*TMP与当前记录进行匹配,然后使用substr()仅打印与该表达式匹配的位,可以删除最后一次出现 后的位。正则表达式将从记录的开头到最后一个记录进行匹配TMP,并且该match()函数将变量设置RLENGTH为该匹配的长度。

由于我们使用双换行符作为输出记录分隔符,因此您将在输出末尾得到一个额外的空行。


如果你的文件看起来像

AAA
CBH
VFh

GFD
DFC
VGF
HGD
tmp/some/path/here
JHG

TFD
PI1
98A
tmp/some/path/here
tmp/some/path/here
765
UBS

...并且您希望基于以 开头的行进行相同类型的转换tmp,然后修改命令中使用的正则表达式,以便它一直匹配到“ -match()末尾的换行符”tmp线”:

$ awk -v RS='' -v ORS='\n\n' 'match($0,".*tmp[^\n]*") { print substr($0,1,RLENGTH) }' file
GFD
DFC
VGF
HGD
tmp/some/path/here

TFD
PI1
98A
tmp/some/path/here
tmp/some/path/here

请注意,我不能 100% 确定情况awk如何应该\n在括号表达式内进行解释,但awk我有权访问的所有实现(OpenBSD awkmawk和 GNE awk)似乎都将其视为换行符和不是作为两个单独的字符\n

答案2

只需反转文本并从正则表达式到正则表达式进行正常打印,然后再次重新反转以获得原始顺序

 tac < file.txt | sed -n '/TMP/,/^$/p' | tac

答案3

如果您可以匹配到第一个TMP。空RS将导致段落模式,其中两个或多个连续\n 字符将用作记录分隔符

$ # sub is used to remove everything after first occurrence of TMP
$ # return value of sub (0 if no match, 1 if match is found) determines
$ # if record should be printed or not
$ # use \nTMP\n to match only whole line
$ awk -v RS= 'sub(/TMP\n.*/, "TMP\n")' ip.txt
GFD
DFC
VGF
HGD
TMP

TFD
PI1
98A
TMP

如果您需要直到最后TMP,您可以使用GNU awk(because of gensub) 或perl

$ # use \nTMP\n to match only whole line
$ # same as: perl -00 -ne 'print if s/.*TMP\n\K.*/\n/s' ip.txt
$ awk -v RS= '/TMP/{print gensub(/(.*TMP\n).*/, "\\1", 1)}' ip.txt
GFD
DFC
VGF
HGD
TMP

TFD
PI1
98A
TMP
TMP

答案4

您已使用 sed 进行标记,因此我们可以按照所示方式进行操作。请注意,我们使用的是 GNU sed。根据我的理解,您希望删除文本文件每个段落中以 tmp (小写)开头的最后一行之后的所有内容。段落是由非空行组成的孤岛,与下一个实例之间至少有一个空行分隔开。

sed -e '
  /./{H;$!d;}
  x;/\ntmp/!d
  :chop
  /\ntmp[^\n]*$/!s/\n[^\n]*$//
  t chop
' file

积累一个段落。当我们到达边界(空行或 eof)时,我们开始检查段落。如果在换行符开头没有看到 tmp,我们会立即删除这一段。否则,我们开始从 para 末尾开始砍掉这些行,直到看到 tmp 行停止处理该 para 并打印它。

使用 GNU awk,我们在 tge 段落模式下操作并将输入字段分隔符设置为换行符。从末尾开始检查字段。当我们看到以 tmp 开头的字段时,我们将 para 缩小到该字段并打印并完成此段落。

awk -F '\n' -v RS= -v OFS='\n' '
  /(^|\n)tmp/ {
    for(i=NF; i; i--) {
      if ($i ~ /^tmp/) {
        NF=i; NF++; print; break
      }
    }
  }
' file

rindex 将告诉子字符串在字符串中从末尾开始的位置。因此,通过 rindex abd 获取最后一个 tmp 的位置,使用该位置来获取下一个最接近的换行符的位置。

perl -p00e '
  s/.*//s,next unless /^tmp/m;
  s/\z/\n/;
  my $p = 1+rindex($_, "\ntmp");
  my $q = 1+index($_, "\n", $p);
  substr($_, $q) = "\n";
' file

相关内容