sed / awk / perl / etc: Mover linhas para trás no arquivo

3

Imagine um arquivo de texto que contenha texto aleatório e dois marcadores únicos

01 text text text
02 text text text
03 __DELETE_THIS_LINE_BEGIN__
04 text text text
05 text text text
06 text text text
07 text text text
08 __DELETE_THIS_LINE_END__
09 four
10 interesting
11 lines
12 follow
13 text text text
14 text text text
15 text text text
16 text text text
17 __DELETE_THIS_LINE_BEGIN__
18 text text text
19 text text text
20 text text text
21 text text text
22 __DELETE_THIS_LINE_END__
23 even
24 more
25 interesting
26 lines

Eu quero uma expressão sed / awk / perl / etc que mova as quatro linhas interessantes após o marcador END para a posição do marcador PREVIOUS BEGIN e também exclua ambos os marcadores. O que deve resultar em:

01 text text text
02 text text text
09 four
10 interesting
11 lines
12 follow
04 text text text
05 text text text
06 text text text
07 text text text
13 text text text
14 text text text
15 text text text
16 text text text
23 even
24 more
25 interesting
26 lines
18 text text text
19 text text text
20 text text text
21 text text text

Os dois marcadores são sempre um par e ocorrem várias vezes no arquivo. O marcador BEGIN sempre vem antes do marcador END.

Não precisa ser um oneliner, eu também usaria scripts perl ou python.

Eu tentei sed com:

sed -e '/__DELETE_THIS_LINE_END__/,+4 {H;d};/__DELETE_THIS_LINE_BEGIN__/ x' <source.txt> > <target.txt>

... o que não funcionou. O primeiro marcador DELETE_THIS_LINE_BEGIN foi excluído (Nada no Buffer para substituição) e o primeiro marcador DELETE_THIS_LINE_END foi movido para a posição do segundo marcador DELETE_THIS_LINE_BEGIN .

Alguma idéia?

    
por sweisgerber.dev 24.04.2014 / 16:29

2 respostas

6

awk:

awk '
    /__DELETE_THIS_LINE_BEGIN__/ {keep=1; next} 
    /__DELETE_THIS_LINE_END__/   {keep=0; move=4; next}
    keep {saved[++s]=$0; next} 
    move-- == 0 {for (i=1; i<=s; i++) print saved[i]; delete saved; s=0}
    1
    END {for (i=1; i<=s; i++) print saved[i]}
' file 
01 text text text
02 text text text
09 four
10 interesting
11 lines
12 follow
04 text text text
05 text text text
06 text text text
07 text text text
13 text text text
14 text text text
15 text text text
16 text text text
23 even
24 more
25 interesting
26 lines
18 text text text
19 text text text
20 text text text
21 text text text

Além disso, com o awk, você pode redefinir o separador de registro:

awk -v RS='\n[0-9]+ __DELETE_THIS_LINE_(BEGIN|END)__\n' '
    NR%2 == 0 {saved=$0; next} 
    {
        n=split($0, lines, "\n")
        for (i=1; i<=4 && i<=n; i++) print lines[i]
        if (saved) print saved
        for (i=5; i<=n; i++) print lines[i]
    }
' file

produz o mesmo resultado.

    
por 24.04.2014 / 17:52
2

Você terá que armazenar em cache as linhas que estão entre os marcadores e inserir o cache após processar as 4 linhas após o marcador final. Em Python (testado com 2.7):

#! /usr/bin/env python
buffer = []
in_block = False
max_interesting_line_nr = 4
begin_marker = "__DELETE_THIS_LINE_BEGIN__"
end_marker = "__DELETE_THIS_LINE_END__"
interesting_line = 0
with open('input') as inf:
    with open('output', 'w') as outf:
        for line in inf:
            if begin_marker in line:
                in_block = True
                continue
            if end_marker in line:
                assert in_block is True
                interesting_line = max_interesting_line_nr
                in_block = False
                continue
            if interesting_line:
                outf.write(line)
                interesting_line -= 1
                if interesting_line == 0:  # output gathered lines
                    for lbuf in buffer:
                        outf.write(lbuf)
                    buffer = []  # empty buffer 
                continue
            if in_block:
                buffer.append(line)  # gather lines
            else:
                outf.write(line)
    
por 24.04.2014 / 17:18