拥有以下文本文件,我需要在两个模式之间提取和打印字符串,并且还包括第一个模式上方的行和第二个模式后面的行
asdgs sdagasdg sdagdsag
asdfgsdagg gsfagsaf
asdfsdaf dsafsdfdsfas
asdfdasfadf
nnnn nnnnn aaaaa
line before first pattern
***** FIRST *****
dddd ffff cccc
wwww rrrrrrrr xxxx
***** SECOND *****
line after second pattern
asdfgsdagg gsfagsaf
asdfsdaf dsafsdfdsfas
asdfdasfadf
nnnn nnnnn aaaaa
我已经找到了许多解决方案,使用sed和awk来提取两个标签,如下所示
sed -n '/FIRST/,/SECOND/p' FileName
但如何在模式之前和之后包含该行?
期望的输出:
line before first pattern
***** FIRST *****
dddd ffff cccc
wwww rrrrrrrr xxxx
***** SECOND *****
line after second pattern
答案 0 :(得分:3)
正如您要求sed
/ awk
解决方案(并且每个人都害怕ed
;-),这是您在awk中执行此操作的一种方式:
awk '/FIRST/{print p; f=1} {p=$0} /SECOND/{c=1} f; c--==0{f=0}' file
匹配第一个模式时,打印上一行p
并设置打印标记f
。当第二个模式匹配时,将c
设置为1.如果f
为1(真),则将打印当前行。 c--==0
仅在第二个模式匹配后的行中为真。
另一种方法是通过循环文件两次:
awk 'NR==FNR{if(/FIRST/)s=NR;else if(/SECOND/)e=NR;next}FNR>=s-1&&FNR<=e+1' file file
第一次通过文件循环遍历文件并记录行号。第二个打印范围内的行。
第二种方法的优点是,只需更改脚本中的数字,就可以轻松打印M行之前和N行之后的行。
要使用shell变量而不是硬编码模式,您可以传递如下变量:
awk -v first="$first" -v second="$second" '...' file
然后使用$0 ~ first
代替/FIRST/
。
答案 1 :(得分:1)
我会说
sed '/FIRST/ { x; G; :a n; /SECOND/! ba; n; q; }; h; d' filename
那是:
/FIRST/ { # If a line matches FIRST
x # swap hold buffer and pattern space,
G # append hold buffer to pattern space.
# We saved the last line before the match in the hold
# buffer, so the pattern space now contains the previous
# and the matching line.
:a # jump label for looping
n # print pattern space, fetch next line.
/SECOND/! ba # unless it matches SECOND, go back to :a
n # fetch one more line after the match
q # quit (printing that last line in the process)
}
h # If we get here, it's before the block. Hold the current
# line for later use.
d # don't print anything.
请注意,BSD sed(与Mac OS X和* BSD一起提供)对分支命令有点挑剔。如果您正在使用其中一个平台,
sed -e '/FIRST/ { x; G; :a' -e 'n; /SECOND/! ba' -e 'n; q; }; h; d' filename
应该有用。
答案 2 :(得分:1)
无论文件中是否有多个范围,这都会有效:
afterRender
如果在FIRST将$ cat tst.awk
/FIRST/ { print prev; gotBeg=1 }
gotBeg {
print
if (gotEnd) gotBeg=gotEnd=0
if (/SECOND/) gotEnd=1
}
{ prev=$0 }
$ awk -f tst.awk file
line before first pattern
***** FIRST *****
dddd ffff cccc
wwww rrrrrrrr xxxx
***** SECOND *****
line after second pattern
更改为数组之前需要打印多行。如果您需要在SECOND之后打印超过1行,请将prev
更改为计数。
答案 3 :(得分:0)
x<-sample(1:10,1e5,rep=TRUE)
y<-sample(1:10,1e5,rep=TRUE)
martin <- function(x, y) {
x = match(x, unique(x)); y = match(y, unique(y))
v = x + (max(x) - 1L) * y
which.max(tabulate(match(v, v)))
}
akrun <-function(x,y) {
library(data.table)
xy<-data.frame(x,y)
setDT(xy)[, .N,list(x,y)][which.max(N)]
}
mucio <-function(x,y){
xy<-data.frame(x,y)
t<-table(xy)
indexes <- which(t == max(t), arr.ind = TRUE)[1,]
x_value <- dimnames(t)$x[indexes["x"]]
y_value <- dimnames(t)$y[indexes["y"]]
rep_number <- max(t)
}
sam<-function(x,y){
library(dplyr)
xy<-data.frame(x,y)
xy %>%
group_by(x, y) %>%
tally() %>%
ungroup %>%
top_n(1)
}
dimitris<-function(x,y){
library(dplyr)
xy<-data.frame(x,y)
xy %>% group_by(x, y) %>% summarise(n=n()) %>%
ungroup %>% filter(n==max(n)) %>% select(-n)
}
microbenchmark(martin(x,y),akrun(x,y),mucio(x,y),sam(x,y),dimitris(x,y),times=5)
Unit: milliseconds
expr min lq mean median uq
martin(x, y) 11.727217 14.246913 41.359218 14.384385 82.639796
akrun(x, y) 4.426462 4.613420 4.866548 4.892432 5.011406
mucio(x, y) 73.938586 74.037568 103.941459 79.516207 145.232870
sam(x, y) 8.356426 8.586212 8.919787 8.586521 8.775792
dimitris(x, y) 8.618394 8.738228 9.252105 9.063965 9.075298
max neval cld
83.797780 5 a
5.389018 5 a
146.982062 5 b
10.293983 5 a
10.764640 5 a
sed '#n
H;$!d
x;s/\n/²/g
/FIRST.*SECOND/!b
s/.*²\([^²]*²[^²]*FIRST\)/\1/
:a
s/\(FIRST.*SECOND[^²]*²[^²]*\)².\{1,\}/\1/
ta
s/²/\
/g
p' YourFile
)--posix
:除非有expres请求(例如#n
)p
:将每一行追加到缓冲区,如果不是最后一行,则删除当前行和循环H;$!d
:加载缓冲区并用其他字符替换任何新行(此处我使用x;s/\n/²/g
)因为posix sed不允许²
[^\n]
:如果没有模式存在,退出没有输出/FIRST.*SECOND/!b
:删除第一个模式之前的所有内容s/.*²\([^²]*²[^²]*FIRST\)/\1/
:goto的标签(稍后使用):a
:删除第二个模式后的一行后的所有内容。它采用最大的字符串,因此模式的最后一次出现是参考s/\(FIRST.*SECOND[^²]*²[^²]*\)².\{1,\}/\1/
:如果发生上一次ta
,则必须标记为s///
。它直到第一个SECOND模式出现在文件中(在FIRST之后)a
:放回新行s/²/\
/g
:打印结果答案 4 :(得分:0)
基于Tom的评论:如果文件不大,我们可以将它存储在数组中,然后循环遍历它:
awk '{a[++i]=$0} /FIRST/{s=NR} /SECOND/{e=NR} END {for(i=s-1;i<e+1;i++) print a[i]}'
答案 5 :(得分:0)
我会亲自与Perl一起做。我们有&#39;范围运营商&#39;我们可以用它来检测我们是否在两种模式之间:
if ( m/FIRST/ .. /SECOND/ )
这很容易。什么不那么容易就是“抓住”#39;前一行和下一行。所以我设置了一个$prev_line
值,这样当我第一次点击那个测试时,我知道要打印什么。我清楚$prev_line
,因为当我再次打印它时它是空的,但也因为那时我可以在范围的末尾发现过渡。
这样的事情:
#!/usr/bin/perl
use strict;
use warnings;
my $prev_line = " ";
while (<DATA>) {
if ( m/FIRST/ .. /SECOND/ ) {
print $prev_line;
$prev_line = '';
print;
}
else {
if ( not $prev_line ) {
print;
}
$prev_line = $_;
}
}
__DATA__
asdgs sdagasdg sdagdsag
asdfgsdagg gsfagsaf
asdfsdaf dsafsdfdsfas
asdfdasfadf
nnnn nnnnn aaaaa
line before first pattern
***** FIRST *****
dddd ffff cccc
wwww rrrrrrrr xxxx
***** SECOND *****
line after second pattern
asdfgsdagg gsfagsaf
asdfsdaf dsafsdfdsfas
asdfdasfadf
nnnn nnnnn aaaaa
答案 6 :(得分:0)
这可能适合你(GNU sed):
sed '/FIRST/!{h;d};H;g;:a;n;/SECOND/{n;q};$!ba' file
如果当前行不是FIRST
,请将其保存在保留空间中并删除当前行。如果该行FIRST
将其附加到已保存的行,然后在打印其他行并退出脚本时打印这两行以及任何其他行,直至SECOND
。