将模式匹配后的几行移动到 shell 中的另一个位置(就在另一个匹配之前)

将模式匹配后的几行移动到 shell 中的另一个位置(就在另一个匹配之前)

举个例子来说明问题。我有一个文件

AAA

BBB

CCC

DDD

EEE

FFF

ABAB

ACAC

GGG

HHH

我想在比赛结束后移动 2 行阿巴布到之前DDD。因此,修改后的文件将如下所示:

AAA

BBB

CCC

ABAB

ACAC

DDD

EEE

FFF

GGG

HHH

寻找一些很酷的方法来处理这个问题,最好使用 sed 。

答案1

注意:我假设你的数据确实如此不是每个条目之间有空行;如果是这样,那么您将需要解决四行问题,即更改++3

使用 GNU ed

$ ed -s file <<EOF
/ABAB/,//+m?DDD?-
,p
q
EOF

在哪里

  • /ABAB/,//+/ABAB/寻址从上一个匹配到的一系列行//加上一行
  • m将指定的行移动到
  • ?DDD?-前一行匹配DDD,减去一行
  • ,p打印整个缓冲区

作为单线,

printf '/ABAB/,//+m?DDD?-\n,p\nq\n' | ed -s file

file就地编辑,请替换,p\nq\n,wq\n(w仪式和q伊特)。

答案2

你想要的sed,所以你可以这样做,如下所示:

sed -e '
    /DDD/,/ABAB/! b
    H;/ABAB/!{$!d;};g
    s/\(\n.*\)\n\(.*\)/\2\1/
' input.txt

这对编辑来说很简单ed

 ed -s input.file -  <<\eof
 /ABAB/m?DDD?-
 wq
 eof

答案3

Python(单读)

读取一个文件并存储两个模式之间的内容可以如下完成:

#!/usr/bin/env python3
import sys

flag=False
vals = []
with open(sys.argv[1]) as fd:
    for line in fd: 
        if line.strip() == "DDD" or flag:
            # encountered line where we should stop appending values
            if line.strip() == "ABAB": 
                flag = False
                # print the line and two others, then move in what was between originally
                print(line.strip())
                for i in range(2):
                    print(fd.readline().strip())
                print("\n".join(vals))
                continue
            # store values while we haven't hit ABAB
            flag = True
            vals.append(line.strip())
            continue

        print(line.strip())

Python(双读)

重新使用原始的 awk 想法,我必须读取文件两次,我们可以在 Python 中执行相同的操作:

#!/usr/bin/env python3
import sys


flag_pos,match = 0,0
vals = []
with open(sys.argv[1]) as fd:
     for index, line in enumerate(fd):
         if line.strip() == "DDD":
             flag_pos = index
         if line.strip() == "ABAB":
             vals.append(line.strip())
             fd.readline()
             vals.append(fd.readline().strip())


with open(sys.argv[1]) as fd:
    for index,line in enumerate(fd):
        if index == flag_pos:
            print("\n\n".join(vals),"\n")
        if line.strip() in vals:
            fd.readline()
            continue
        print(line.strip()) 

该脚本可以另存为movelines.py并调用为./movelines.py input.txt

AWK

gawk可能比以下更容易实现sed

$ awk 'NR==FNR && $0=="ABAB" { a[i++]=$0;getline;getline; a[i++]=$0; }; NR!=FNR { if($0=="DDD") for(val in a) printf "%s\n\n",a[val];  if($0 == "ABAB") {getline;getline;getline;} print $0   }' input.txt input.txt
AAA

BBB

CCC

ABAB

ACAC

DDD

EEE

FFF


GGG

HHH

这里的技巧是我们将文件传递awk两次以进行读取,并区分第一次读取找到我们想要移动的行和第二次读取我们实际移动它们的位置。

如果您的实际文件没有如您提供的示例中所示的空行,则您只需要一个getline而不是两个空行,并且"%s\n"在代码的第二部分中就足够了。

为了便于阅读,这里是带有注释的代码的多行版本:

# on first reading NR != FNR, 
# so lets store ABAB and the other line into array
awk 'NR==FNR && $0=="ABAB" { 
        # i variable will be initialized once incremented first time
        a[i++]=$0;getline;getline; a[i++]=$0; 
    };
    # Here we are reading the same file second time 
    NR!=FNR { 

        if($0=="DDD") 
            for(val in a) 
                printf "%s\n\n",a[val]; 
        # Skip what we matched already
        if ($0 == "ABAB"){
            getline;
            getline;
            getline;
        }
        print $0   
     }' input.txt input.txt

答案4

我已经通过以下方法完成了

命令

Step1: h=`sed -n '/[A-Za-z]\{4\}/p' filename| sed -n '1p'`
step2:m=`sed -n '/[A-Za-z]\{4\}/p' filename| sed -n '2p'`
step3

    sed '/[A-Z]\{4\}/d' filename|sed "/CCC/s/.*/&\n\n$h\n\n$m/g"| sed '/^$/d' 

输出

AAA
BBB
CCC
ABAB
ACAC
DDD
EEE
FFF
GGG
HHH

相关内容