sed -e '/XXXX/,+4d' fv.out
我需要在文件中找到一个特定的模式,并同时删除它上面的5行和下面的4行。我发现删除上面的一行会同时删除包含该模式的行和下面的四行。
sed -e '/XXXX/,~5d' fv.out
在sed手册中,它给出了一个波浪线(~),表示该行后面跟着的是模式。但是当我尝试时,被删除的是跟在模式后面的行。
那么,我该如何同时删除包含模式的行上面5行和下面4行呢?
一种使用sed
的方法,假设模式之间不太接近:
script.sed
文件的内容:
## If line doesn't match the pattern...
/pattern/ ! {
## Append line to 'hold space'.
H
## Copy content of 'hold space' to 'pattern space' to work with it.
g
## If there are more than 5 lines saved, print and remove the first
## one. It's like a FIFO.
/\(\n[^\n]*\)\{6\}/ {
## Delete the first '\n' automatically added by previous 'H' command.
s/^\n//
## Print until first '\n'.
P
## Delete data printed just before.
s/[^\n]*//
## Save updated content to 'hold space'.
h
}
### Added to fix an error pointed out by potong in comments.
### =======================================================
## If last line, print lines left in 'hold space'.
$ {
x
s/^\n//
p
}
### =======================================================
## Read next line.
b
}
## If line matches the pattern...
/pattern/ {
## Remove all content of 'hold space'. It has the five previous
## lines, which won't be printed.
x
s/^.*$//
x
## Read next four lines and append them to 'pattern space'.
N ; N ; N ; N
## Delete all.
s/^.*$//
}
运行方式:
sed -nf script.sed infile
awk '$0 ~ "XXXX" { lines2del = 5; nlines = 0; }
nlines == 5 { print lines[NR%5]; nlines-- }
lines2del == 0 { lines[NR%5] = $0; nlines++ }
lines2del > 0 { lines2del-- }
END { while (nlines-- > 0) { print lines[(NR - nlines) % 5] } }' fv.out
更新:
以下是该脚本的解释:
lines
中的最后5行。$0 ~ "XXXX
;$0
为当前记录:在此情况下为一行;~
为扩展正则表达式匹配运算符),则重置读取的行数并记录有5行需要删除(包括当前行)。我的原始脚本版本如下,但最终我对其进行了优化:
awk '$0 ~ "XXXX" { lines2del = 5; nlines = 0; }
lines2del == 0 && nlines == 5 { print lines[NR%5]; lines[NR%5] }
lines2del == 0 && nlines < 5 { lines[NR%5] = $0; nlines++ }
lines2del > 0 { lines2del-- }
END { while (nlines-- > 0) { print lines[(NR - nlines) % 5] } }' fv.out
awk
是一款非常好用的工具!我强烈建议你在互联网上找到一篇教程并阅读。其中一个重要的事情是:awk
使用扩展正则表达式(ERE)。它们的语法与sed
中使用的标准正则表达式(RE)略有不同,但所有可以用RE做的事情都可以用ERE完成。
这个想法是读取5行而不打印它们。如果你找到了模式,删除未打印的行和下面的4行。如果你没有找到模式,请记住当前行并打印第一行未打印的行。最后,打印未打印的内容。
sed -n -e '/XXXX/,+4{x;s/.*//;x;d}' -e '1,5H' -e '6,${H;g;s/\n//;P;s/[^\n]*//;h}' -e '${g;s/\n//;p;d}' fv.out
当然,这仅适用于文件中只有一个模式的情况。如果有多个模式,则需要在找到模式后读取5行新行,并且如果这些行中再次出现模式,则会变得复杂。在这种情况下,我认为sed不是正确的工具。
这个可能适合你:
sed 'H;$!d;g;s/\([^\n]*\n\)\{5\}[^\n]*PATTERN\([^\n]*\n\)\{5\}//g;s/.//' file
或者这样:
awk --posix -vORS='' -vRS='([^\n]*\n){5}[^\n]*PATTERN([^\n]*\n){5}' 1 file
sed ':a;/PATTERN/,+4d;/\([^\n]*\n\)\{5\}/{P;D};$q;N;ba' file
vim
可以高效地完成这项任务:vim -c 'g/pattern/-5,+4d' -c 'w! outfile|q!' infile
或者
vim -c 'g/pattern/-5,+4d' -c 'x' infile
在原地编辑文件。