将3行连接到与模式匹配的行

时间:2014-10-30 16:09:57

标签: bash perl vim awk sed

我在VI编辑器中有一个文件,如下所示:

I1 a b c d e f 
g h i j k l m     
o p q r s t u v     
w x y z     
I2 a b c d     
e f g h i j k l m     
n o p q r s t     
u v w x y z    
I3 a  b c d e     
f g h i j k l m n     
o p q r s t u v w x y z 

我正在尝试合并以I(^ I)开头的行之后的3行:

I1 a b c d e f g h i j k l m n o p q r s t u v w x y z     
I2 a b c d e f g h i j k l m n o p q r s t u v w x y z    
I3 a b c d e f g h i j k l m n o p q r s t u v w x y z 

我已经用Google搜索,看看我是否可以在VI编辑器中包含I1的行(即I [^I]之后的行)之后加入3行,并找到了Join将下一行连接到当前行的命令,如:g/^I/norm Jx。但我想使用此命令将接下来的3行连接到当前行。

如果有人能通过VI Editor或任何脚本语言向我建议一种方法,我们将不胜感激。

8 个答案:

答案 0 :(得分:4)

以下是使用awk的一种方法:

awk 'NR>1&&/^I[0-9]/{print ""}{printf "%s", $0}END{print ""}' file.txt

当行号大于1且行开头为" I"后跟一个数字,使用print ""打印换行符。使用printf打印每行的内容。在END块中(感谢fedorqui),打印最终换行符。

在您的文件上测试它:

$ awk 'NR>1&&/^I[0-9]/{print ""}{printf "%s", $0}END{print ""}' file.txt
I1 a b c d e fg h i j k l mo p q r s t u vw x y z
I2 a b c de f g h i j k l mn o p q r s tu v w x y z
I3 a b c d ef g h i j k l m no p q r s t u v w x y z

答案 1 :(得分:3)

你几乎就在那里:

:g/^I/norm 4J
  • :g采用正则表达式和命令
  • :norm 4J是" ex"执行"正常模式的命令"命令。即你输入的内容。

请参阅:help :g:help :norm


基于彼得的评论::g/^I/.,+3join:g/^I/j4

答案 2 :(得分:2)

如果您确定每个块具有相同的行数,请说4:

:g/^I/norm 4J

如果您无法确定要加入的行数,则问题会变得稍微复杂一些:

:g/^I/norm O    " separates every block with a blank line
:g//vipJ        " join each block into it's own line         
:g/^$/d         " removes every blank line

如果你不介意在框外思考一下,这个命令适用于任何块大小:

:%join|s/ I/\rI/g    " join the whole buffer into one single line
                     " then substitute every ' I' with `\rI` 

答案 3 :(得分:2)

tr -d "\n" <filename | sed 's/ \+/ /g;s/ I/\nI/g'

输出:

I1 a b c d e f g h i j k l m o p q r s t u v w x y z
I2 a b c d e f g h i j k l m n o p q r s t u v w x y z
I3 a b c d e f g h i j k l m n o p q r s t u v w x y z 

答案 4 :(得分:1)

这个awk成就了:

awk '/^I[0-9]/ {if (f) print f; f=""} {f=sprintf("%s%s", (f?f FS:""), $0)} END {print f}' file

它不断将行添加到变量f中。当找到以I +数字开头的行时,它会打印出来。

给定输入返回:

I1 a b c d e f g h i j k l m o p q r s t u v w x y z
I2 a b c d e f g h i j k l m n o p q r s t u v w x y z
I3 a  b c d e f g h i j k l m n o p q r s t u v w x y z

答案 5 :(得分:1)

Perlish答案;

#!/usr/bin/perl

use strict;
use warnings;

while ( <DATA> ) {
   chomp;
   s/\s+/ /g;
   if ( m/^I/ ) { print "\n" };
   print;
}

__DATA__
I1 a b c d e f 
g h i j k l m     
o p q r s t u v     
w x y z     
I2 a b c d     
e f g h i j k l m     
n o p q r s t     
u v w x y z    
I3 a  b c d e     
f g h i j k l m n     
o p q r s t u v w x y z 

答案 6 :(得分:1)

纯粹的bash解决方案:

#! /bin/bash

start="I"
cur=
while read line ; do
    if test "${line:0:1}" = "$start" ; then
        test "$cur" = "" || { echo "$cur" ; cur= ;}
    fi
    cur+="$line"
done << EOT
I1 a b c d e f 
g h i j k l m     
o p q r s t u v     
w x y z     
I2 a b c d     
e f g h i j k l m     
n o p q r s t     
u v w x y z    
I3 a  b c d e     
f g h i j k l m n     
o p q r s t u v w x y z 
EOT

echo "$cur"

答案 7 :(得分:0)

在Vim上,除了glen和romainl指向的ex命令外,你还可以录制一个宏:

qm/^I<enter>vnJq

然后重复两次:

2@m

解释

  • qm - 开始在给定的寄存器上录制宏(在这种情况下为m
  • / ^我输入 - 搜索正则表达式^I
  • v - 开始视觉选择
  • n - 在下一次出现正则表达式之前选择文本(如果行数固定,则此步骤可以替换为V3j的前一步)
  • J - 加入选定的行
  • q - 停止录制宏