我想在匹配的模式之前和之后评论(#)6行。 我提到了这个问题。
How do I delete a matching line, the line above and the one below it, using sed?
我尝试使用保持缓冲区来解决此问题,但无效。
我在文件中多次出现以下序列:
aaaa
bbbb
cccc
dddd
eeee
ffff
gggg
hhhh
iiii
jjjj
kkkk
llll
mmmm
nnnn
oooo
如果我搜索hhhh
,则输出文件应在下面给出:
aaaa
#bbbb
#cccc
#dddd
#eeee
#ffff
#gggg
#hhhh
#iiii
#jjjj
#kkkk
#llll
#mmmm
#nnnn
oooo
请帮我用sed或任何其他脚本来做这件事!!!
答案 0 :(得分:12)
这个问题被标记为Vim,所以...我心爱的:help :global
和:help :normal
来救援!
:g/hhhh/-6,+6norm I#
:substitute
变体:
:g/hhhh/-6,+6s/^/#
故障:
:global
命令用于为与给定模式匹配的每一行执行Ex命令。
:g/hhhh/d
会删除包含hhhh
。
Ex命令通常接受可选范围。范围可以使用绝对行号5,15
和/或相对行号-3,+41
。
:g/hhhh/-6,+6d
会删除包含hhhh
的每一行以上6行和6行之间的所有内容。
:normal
命令允许我们从命令行执行常规命令,并接受范围,就像其他Ex命令一样。 I#
是在行的开头插入#
的最简单方法,因此我们可以从命令行执行:normal I#
,这将我们带到第一个解决方案:
:g/hhhh/-6,+6norm I#
作为Ex命令,:substitute
也接受范围,因此我们也可以使用它来在范围中的每一行的开头插入#
,这将我们带到第二个解决方案:
:g/hhhh/-6,+6s/^/#
答案 1 :(得分:4)
要在Perl中执行此操作,您需要将整个文件读入数组,然后找到匹配行的索引并编辑周围的行,这可以通过范围完成。
您必须从数组切片中删除未定义的值,否则如果您的匹配位于文件的开头或结尾附近(即少于6行),您将创建新条目。
perl -we '@a = <>; # read whole file
for (0 .. $#a) { # loop over indexes
if ($a[$_] =~ /hhhh/) { # find match
s/^/#/ for grep defined, @a[$_-6 .. $_+6] # edit
}
}; print @a" hhh.txt
在for循环$_
中,别名为元素,这就是我们可以直接对其应用替换s///
的原因。
使用Tie::File
可以简化这一点。
<强>输出:强>
aaaa
#bbbb
#cccc
#dddd
#eeee
#ffff
#gggg
#hhhh
#iiii
#jjjj
#kkkk
#llll
#mmmm
#nnnn
oooo
答案 2 :(得分:2)
这可能适合你(GNU sed):
sed -r ':a;s/\n/&/6;tb;$!{N;ba};:b;/SEARCH_STRING/!{P;D};s/\n/&/12;tc;$!{N;bb};:c;s/^/#/gm' file
答案 3 :(得分:0)
**这里有一个Perl解决方案! **
我会将整个内容存储在一个Array中,遍历数组并在模式匹配时标记迭代器变量。然后从迭代器变量中删除并添加6,然后我们去,如果给出了标记变量,则可以在该行前面连接主题标签。
使其更清晰:
use File::Slurp;
my $find_counter = 0;
my $line_counter = 0;
my @lines = read_file( 'filename' ) ;
foreach my $line (@lines) { # foreach or for loop
if ($line =~ /$pattern/) {
$file_counter = $line_counter;
last;
}
$line_counter++;
}
# loop again through @lines and when the line is between
# $file_counter + - 6 , concat the hashtag in front of the line
答案 4 :(得分:0)
另一种方法是一次读取一行,这对于大文件可能更好,它可以避免将整个文件读入内存。
数组@prev_lines
包含匹配前要打印的行数。找到匹配项后,使用#
前缀打印记住的行,并将$num_line_to_print
设置为匹配后要打印的行数。如果该行不匹配,则查看是否要为上一个匹配打印行。如果两者都未将该行推入阵列,以防将来匹配。如果数组现在有太多行,它们不匹配匹配,所以只需打印它们。最后,在while
循环之后只打印出任何保存行。
use strict;
use warnings;
my $num_lines_wanted = 6;
my @prev_lines;
my $num_line_to_print = 0;
while ( <> ) {
if ( m/hhhh/ ) {
while ( scalar(@prev_lines) > 0 ) {
print "#", shift @prev_lines;
}
print "#", $_;
$num_line_to_print = $num_lines_wanted;
}
elsif ( $num_line_to_print > 0 ) {
print "#", $_;
$num_line_to_print--;
}
else {
push @prev_lines, $_;
if ( scalar(@prev_lines) > $num_lines_wanted ) {
print shift @prev_lines;
}
}
}
while ( scalar(@prev_lines) > 0 ) {
print shift @prev_lines;
}
原始问题并不清楚如何处理输入,其中两条hhhh
行在彼此的六行之内。这里的代码重新开始每次匹配的编号,它只打印输入行一次,即使一行在两个#
匹配的范围内,也只添加一个hhhh
。
答案 5 :(得分:0)
对于那些对sed感到满意的人,我建议使用grep
将上下文传送到sed
以创建一些简单的sed
命令:
<强> grep -A6 -B6 -n hhhh file | sed -e 's/[^0-9].*$//' -e 's/$/s|^|#|/' | sed -f- file
强>
(以下示例包含-A1
和-B1
以缩短此输出的长度。)
获取一行后的-A1
行和-B1
一行前面的匹配行grep -A1 -B1 -n hhhh file
输出:
7-gggg 8:hhhh 9-iiii
...我们将变成sed命令,用| sed -e 's/[^0-9].*$//' -e 's/$/s|^|#|/'
注释掉那些带编号的行,两个sed命令删除第一个非数字后的所有内容,并用该替换该缩短行的结尾| sed -e 's|^|#|'
,替换整个行的注释。得到了这个:
7s|^|#| 8s|^|#| 9s|^|#|
...我们希望将这些命令传递给sed,因此我们使用等同于-f-
的{{1}}并指示sed从stdin读取命令。
-f /dev/stdin
aaaa bbbb cccc dddd eeee ffff #gggg #hhhh #iiii jjjj kkkk llll mmmm nnnn oooo