删除两个模式(sed或awk?)之间出现的所有新行



我有一个简单的问题。我需要去掉它们在两个模式之间出现的所有次数。ie .

<INFOSTART
A=1
B=2
C=3
D=4
<INFOEND
<INFOSTART
G=1
Z=3
<INFOEND

所以我希望输出类似于

A=1 B=2 C=3 D=4
G=1 Z=3

知道我该怎么做吗?提前感谢各位。

您可以对awk使用一个简单的状态机,例如下面的输入文件,稍微修改一下,也允许在标记之外的文本(如果没有这样的文本,它仍然会按照期望工作,这只是为了处理额外的情况):

xyzzy
plugh
<INFOSTART
A=1
B=2
C=3
D=4
<INFOEND
twisty
passages
<INFOSTART
G=1
Z=3
<INFOEND
after
last

对于这样的数据文件(或您的原始文件),下面的awk命令可以满足您的需要,将开始和结束标记之间的行组合成一行:

awk ' /^<INFOSTART$/ {inside=1; sep=""; next}
      /^<INFOEND$/   {inside=0; print ""; next}
      inside         {printf sep""$0; sep=" "; next}
                     {print}' input_file
xyzzy
plugh
A=1 B=2 C=3 D=4
twisty
passages
G=1 Z=3
after
last

更详细地检查awk代码,下面的部分在每行展开。


当您发现仅包含开始标记的行时,就运行以下段。它将inside状态设置为true(非零),表示您应该开始合并行,并将初始分隔符设置为空字符串,以确保合并行上没有前导空格。next简单地立即获取下一个输入行,开始一个新的循环:

/^<INFOSTART$/ {inside=1; sep=""; next}

假设您没有找到开始标记,则此段为结束标记运行。如果找到,则将inside状态设置回false(零),以开始打印与输入文件中出现的行完全相同的行。它还输出一个换行符以正确结束合并行,然后使用下一个输入行重新开始循环:

/^<INFOEND$/   {inside=0; print ""; next}

如果您已经确定该行既不是开始标记也不是结束标记,则您的行为取决于inside状态。如果为true,则需要将输入行合并为单个输出行,因此只需打印分隔符,后面不加换行符,后跟行本身。然后将分隔符设置为空格,以便下一个输入行将与前一个输入行正确分隔。然后返回到下一个输入行:

inside         {printf sep""$0; sep=" "; next}

最后,如果你到了这里,你知道你已经在开始/结束部分之外了,所以你只需要按照输入文件中存在的样子回调该行:

               {print}'

如果您不想要格式化良好的版本,可以使用以下缩小版本,假设您确定只有<INFO...行是开始和结束标记:

awk '/^<INFOS/{a=1;b="";next}/^<INFOE/{a=0;print"";next}a{printf b$0;b=" ";next}1'

但是,由于这可能是在脚本中而不是在一行命令中,所以我倾向于坚持使用可读的版本。

trsed:

AMD$ tr 'n' ' ' < File | sed 's/<INFOSTART //g; s/<INFOEND /n/g'
A=1 B=2 C=3 D=4
G=1 Z=3

先用space替换所有newlines。然后使用sed删除所有<INFOSTART,并用newlines替换所有<INFOEND

Perl:

< input perl -ne 's/n/ /, print if $s = /<INFOSTART/ .. ($e = /<INFOEND/)
                                    and $s > 1 and !$e;
                  print "n" if $e'

$s在标签之间为真(使用..运算符)。$e在匹配结束标签时为真,$s在匹配开始标签时为1。

这可能适合您(GNU sed):

sed '/^<INFOSTART/d;:a;N;/^<INFOEND/M!s/n/ /;ta;P;d' file

删除以<INFOSTART<INFOEND开头的行,并用空格替换所有其他行之间的换行符。

该解决方案可以进一步缩减(如果文件格式良好)为:

sed '/^</d;:a;N;/^</M!s/n/ /;ta;P;d' file

最新更新