sed/awk/perl/etc: 파일에서 줄을 뒤로 이동

sed/awk/perl/etc: 파일에서 줄을 뒤로 이동

임의의 텍스트와 두 개의 고유 태그가 포함된 텍스트 파일을 상상해 보세요.

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

END 태그 뒤의 흥미로운 네 줄을 다음으로 이동하는 sed/awk/perl/etc 표현식을 원합니다.더 일찍BEGIN은 두 표시를 모두 표시하고 삭제합니다. 결과는 다음과 같습니다.

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

이 두 태그는 항상 쌍이며 파일에 여러 번 나타납니다. BEGIN 태그는 항상 END 태그 앞에 옵니다.

꼭 하나의 라이너일 필요는 없으며 Perl이나 Python 스크립트도 사용할 것입니다.

나는 sed를 사용해 본다:

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

...이건 작동하지 않습니다. 첫 번째DELETE_THIS_LINE_BEGIN표시가 제거되고(버퍼에 교체할 ​​항목이 없음) 첫 번째DELETE_THIS_LINE_END마커가 두 번째 위치로 이동했습니다.DELETE_THIS_LINE_BEGIN표시.

어떤 아이디어가 있나요?

답변1

이상한:

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

또한 awk를 사용하면 레코드 구분 기호를 재정의할 수 있습니다.

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

동일한 결과가 생성됩니다.

답변2

태그 사이의 라인을 캐시하고 닫는 태그 뒤 4라인을 처리한 후 캐시를 삽입해야 합니다. Python(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)

관련 정보