考虑一个文本文件users.txt
:
#alice
#bob
charlie
dotan
eric
我需要注释从(不包括)最后一个注释行到(包括)的所有内容dotan
。这是结果:
#alice
#bob
#charlie
#dotan
eric
有没有一个好的sed
oneliner 可以做到这一点?我会对任何工具感到满意,不仅仅是sed
,真的。
目前我正在获取最后注释行的行号,如下所示:
$ cat -n users.txt | grep '#' | tail -n1
2 #bob
然后我添加一个并评论sed
:
$ sed -i'' '3,/dotan/ s/^/#/' users.txt
我知道我可以聪明点,把这一切和一些东西放在一起,bc
写成一句丑陋的俏皮话。当然必须有一种更清洁的方法吗?
答案1
如果现有的注释行形成一个连续的块,那么您可以从第一的相反,注释行,仅注释掉那些直到并包括尚未注释的结束模式的行
sed '/^#/,/dotan/ s/^[^#]/#&/' file
如果现有注释不连续,那么由于 sed 范围匹配的贪婪本质,我认为您需要执行类似的操作
tac file | sed '/dotan/,/^#/ s/^[^#]/#&/' | tac
即匹配向上从结束模式到“第一个”注释 - 显然,如果您想要一个就地解决方案,那么这不是那么方便。
答案2
怎么样
perl -pe '$n=1 if s/^dotan/#$&/; s/^[^#]/#$&/ unless $n==1;' file
或者,在 awk 中也有同样的想法:
awk '(/^dotan/){a=1; sub(/^/,"#",$1)} (a!=1 && $1!~/^#/){sub(/^/,"#",$1);}1; ' file
答案3
您可以通过一次调用来处理这两种情况(单个连续块中的注释行或散布在未注释行之间)sed
:
sed '1,/PATTERN/{/^#/{x;1d;b};//!{H;/PATTERN/!{1h;d};//{x;s/\n/&#/g}}}' infile
这仅处理范围内的行1,/PATTERN/
。它x
改变了保持空间w。每次注释一行时都会使用模式空间(因此保留缓冲区中永远不会有超过一个注释行)并将未注释的每一行附加到H
旧空间(当在第一行时,1d
和 分别1h
还需要删除初始行)保持缓冲区中的空行)。
当它到达与 PATTERN 匹配的行时,它还将其附加到H
旧缓冲区,ex
更改缓冲区,然后用ewline 和 a替换\n
模式空间中的每个 ewline 字符(也就是说,模式空间中的所有行现在都以,包括第一行作为保留空间中的第一行始终是注释行)。 有样品:\n
#
#
infile
alice
#bob
bill
#charlie
ding
dong
dotan
jimmy
#garry
跑步:
sed '1,/dotan/{ # if line is in this range -start c1
/^#/{ # if line is commented -start c2
x # exchage hold space w. pattern space
1d # if 1st line, delete pattern space
b # branch to end of script
} # -end c2
//!{ # if line is not commented -start c3
H # append to hold space
/dotan/!{ # if line doesn't match dotan -start c4
1h # if 1st line, overwrite hold space
d # delete pattern space
} # -end c4
//{ # if line matches dotan -start c5
x # exchage hold space w. pattern space
s/\n/&#/g # add # after each newline character
} # -end c5
} # -end c3
}' infile # -end c1
输出:
alice
#bob
bill
#charlie
#ding
#dong
#dotan
jimmy
#garry
因此它只注释从(并排除)#charlie
到(并包括)的行dotan
,而其他行保持不变。
当然,这假设在匹配的行之前始终至少有一个注释行PATTERN
。如果不是这种情况,您可以在更换之前添加额外的检查:/^#/{s/\n/&#/g}
答案4
这是另一个sed
:
sed -e:n -e'/\n#.*\ndotan/!{$!{N;/^#/bn' \
-eb -e\} -e'/^#/s/\(\n\)\(dotan.*\)*/\1#\2/g' \
-et -e\} -eP\;D <in >out
这正如你所要求的那样。它仅在堆栈上工作 - 在必要时构建它,并在注释行出现之间持续必要的时间,并在找到注释行时转储旧缓冲区以支持输入中的新注释行。图片...
抱歉,我不知道我为什么这么做。但我想到了。
无论如何,sed
在任何系列中的每个最后注释行之间分布其缓冲区,永远不会在缓冲区中保留比准确跟踪最后注释事件所需的更多的内容,并且如果在任何时候遇到最后一行,那么它将尝试最终的g
局部执行语句并分支t
将整个缓冲区打印出来,否则它将P
立即打印从缓冲区中释放的所有行。
我想这就是让我想起手风琴的原因......
printf %s\\n \#alice \#bob charlie dotan eric \
\#alice \#bob charlie dotan eric \
\#alice \#bob charlie dotan eric |
sed -e:n -e'l;/\n#.*\ndotan/!{$!{N;/^#/bn' \
-eb -e\} -e'/^#/s/\(\n\)\(dotan.*\)*/\1#\2/g' \
-et -e\} -eP\;D
#alice
#alice\n#bob$
#alice\n#bob\ncharlie$
#alice\n#bob\ncharlie\ndotan$
#alice
#bob\ncharlie\ndotan$
#bob\ncharlie\ndotan\neric$
#bob\ncharlie\ndotan\neric\n#alice$
#bob\ncharlie\ndotan\neric\n#alice\n#bob$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
#bob
charlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
charlie
dotan\neric\n#alice\n#bob\ncharlie\ndotan$
dotan
eric\n#alice\n#bob\ncharlie\ndotan$
eric
#alice\n#bob\ncharlie\ndotan$
#alice
#bob\ncharlie\ndotan$
#bob\ncharlie\ndotan\neric$
#bob\ncharlie\ndotan\neric\n#alice$
#bob\ncharlie\ndotan\neric\n#alice\n#bob$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
#bob
charlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
charlie
dotan\neric\n#alice\n#bob\ncharlie\ndotan$
dotan
eric\n#alice\n#bob\ncharlie\ndotan$
eric
#alice\n#bob\ncharlie\ndotan$
#alice
#bob\ncharlie\ndotan$
#bob\ncharlie\ndotan\neric$
#bob
#charlie
#dotan
eric
这个命令和上面的命令只有一处不同,那就是l
顶部的 ook 命令。当我们l
观察sed
的模式空间的工作原理时,我们可以更好地了解幕后发生的事情,并更好地理解如何指导其工作。
在这种情况下,我们可以观察sed
堆栈输入,直到找到第二次出现的\n#.*\ndotan
输入,并且当它开始一次一行打印前一个输入时。有点酷。我在这方面学到了很多东西。