역방향으로 grep하고 "이전" 및 "이후" 줄을 ​​제외하는 방법

역방향으로 grep하고 "이전" 및 "이후" 줄을 ​​제외하는 방법

다음 항목이 포함된 텍스트 파일을 고려해 보세요.

aaa
bbb
ccc
ddd
eee
fff
ggg
hhh
iii

패턴(예 fff: )이 주어지면 위 파일을 grep하여 출력을 얻고 싶습니다.

all_lines except (pattern_matching_lines  U (B lines_before) U (A lines_after))

예를 들어, B = 2및 인 경우 A = 1패턴 =의 출력은 다음 fff과 같아야 합니다.

aaa
bbb
ccc
hhh
iii

grep이나 기타 명령줄 도구를 사용하여 이 작업을 수행하려면 어떻게 해야 합니까?


내가 시도할 때 주의할 점은 다음과 같습니다.

grep -v 'fff'  -A1 -B2 file.txt

나는 내가 원하는 것을 얻지 못했습니다. 대신 나는 다음을 얻습니다.

aaa
bbb
ccc
ddd
eee
fff
--
--
fff
ggg
hhh
iii

답변1

gnu grep및 를 사용하여 제외하려는 파일 부분을 정확하게 인쇄할 수 있지만 줄 번호를 인쇄하는 스위치를 추가한 -A다음 출력 형식을 지정하고 명령 스크립트로 전달하여 해당 줄을 제거합니다.-B-nsed

grep -n -A1 -B2 PATTERN infile | \
sed -n 's/^\([0-9]\{1,\}\).*/\1d/p' | \
sed -f - infile

grep이는 다음을 통해 전달된 스키마 파일에도 적용됩니다. -f예:

grep -n -A1 -B2 -f patterns infile | \
sed -n 's/^\([0-9]\{1,\}\).*/\1d/p' | \
sed -f - infile

3개 이상의 연속된 줄 번호를 범위로 축소하여 예를 들어 ... 대신 사용하면 약간 최적화될 수 있다고 생각 2,6d하지만 2d;3d;4d;5d;6d입력에 일치하는 항목이 몇 개만 있으면 수행할 가치가 없습니다.



행 순서를 유지하지 않고 속도가 느려지는 다른 방법은 다음 과 같습니다 comm.

comm -13 <(grep PATTERN -A1 -B2 <(nl -ba -nrz -s: infile) | sort) \
<(nl -ba -nrz -s: infile | sort) | cut -d: -f2-

comm정렬된 입력이 필요합니다. 즉, 줄 순서는 최종 출력에서 ​​유지되지 않습니다(파일이 이미 정렬되지 않은 경우). 따라서 nl정렬하기 전에 줄 번호를 매기고 comm -13고유한 줄만 인쇄하는 데 사용됩니다.두 번째 파일그런 다음 cut추가된 부분( nl예: 첫 번째 필드 및 구분 기호 :) 을 제거합니다
.join

join -t: -j1 -v1 <(nl -ba -nrz -s:  infile | sort) \
<(grep PATTERN -A1 -B2 <(nl -ba -nrz -s:  infile) | sort) | cut -d: -f2-

답변2

대부분의 경우에는 이 작업을 수행하지 않는 것이 더 나을 수 있지만 파일이 다음과 같은 경우에만 가능합니다.진짜sed크네요. 그렇게 큰 스크립트 파일은 처리 할 수 없습니다 .(이는 약 5000줄 이상의 스크립트에서 발생할 수 있습니다), 일반적인 것은 다음과 같습니다 sed.

sed -ne:t -e"/\n.*$match/D" \
    -e'$!N;//D;/'"$match/{" \
            -e"s/\n/&/$A;t" \
            -e'$q;bt' -e\}  \
    -e's/\n/&/'"$B;tP"      \
    -e'$!bt' -e:P  -e'P;D'

이것은 소위 말하는 예입니다.슬라이딩 윈도우입력에. 그것이 작동하는 방식은시야$B버퍼링 - 인쇄를 시도하기 전에 라인 수를 계산합니다.

실제로 이전 요점을 명확히 해야 할 것 같습니다. 이 솔루션과 다른 솔루션의 주요 성능 제한 요소는 간격과 직접적으로 관련됩니다. 이 솔루션은 더 큰 간격으로 인해 속도가 느려집니다.크기, 시간 간격이 증가하면 속도가 느려집니다.빈도. 즉, 입력 파일이 매우 크더라도 실제 간격 발생이 여전히 매우 드물다면 그의 솔루션이 올바른 선택일 수 있습니다. 그러나 간격 크기가 상대적으로 관리하기 쉽고 자주 발생할 가능성이 있는 경우 이 솔루션을 선택해야 합니다.

워크플로는 다음과 같습니다.

  • $match패턴 공간에서 앞의 ewline이 발견 되면 \n그 앞의 각 ewline은 재귀적으로 제거 sed됩니다 .D\n
    • 이전에 패턴 공간을 완전히 비웠 $match는데, 겹치는 부분을 쉽게 처리하려면 랜드마크를 남겨 두는 것이 더 잘 작동하는 것 같습니다.
    • 나는 또한 s/.*\n.*\($match\)/\1/한 번에 그것을 얻고 루프를 피하려고 노력 했지만 $A/$B그것이 클 때 D엘레테 루프가 훨씬 더 빠른 것으로 나타났습니다.
  • N그런 다음 ewline 구분 기호 앞에 있는 추가 입력 줄을 가져오고 \n최근에 사용한 정규식을 참조하여 a를 제거하려고 다시 시도합니다 D./\n.*$match///
  • 패턴 공간이 일치하면 줄의 시작 부분에서만 이 작업을 수행 $match할 수 있습니다 . 이전 줄은 모두 지워졌습니다. $match$B
    • 그래서 우리는 사이클을 시작합니다 $A.
    • 이 루프를 실행할 때마다 우리는 자신을 패턴 공간의 줄 문자로 s///바꾸려고 시도하며 &, 성공하면 est는 우리와 전체 백 버퍼를 스크립트에서 완전히 분기하여 다음 입력으로 맨 위에서 다시 시작합니다. 라인(있는 경우).$A\nt$A
    • test가 실패 하면 op 레이블로 돌아가서 다른 입력 줄에서 반복합니다. bfter를 수집하는 동안 이런 일이 발생하면 :t루프를 다시 시작할 수도 있습니다 .$match$A
  • 함수 루프를 통과하면 마지막 줄(마지막 줄인 경우)을 인쇄 $match하려고 시도하고 , 그렇지 않은 경우 패턴 공간의 줄 문자로 자신을 바꾸려고 시도합니다 .p$!s///&$B\n
    • 또한 이를 테스트하고 성공하면 rint 태그 t로 이동합니다 .:P
    • 그렇지 않은 경우 op로 다시 분기 :t하고 다른 입력 라인을 버퍼에 추가합니다.
  • :P린트 하도록 두면 패턴 공간에서 첫 번째 줄을 P린트한 후 D제거 하고 나머지 내용과 함께 맨 위에서 스크립트를 다시 실행합니다.\n

그렇다면 이번에는A=2 B=2 match=5; seq 5 | sed...

rint의 첫 번째 반복을 위한 패턴 공간은 :P다음과 같습니다.

^1\n2\n3$

이것이 sed프론트 버퍼가 수집되는 $B방식 입니다. 따라서 출력 카운트 라인 sed으로 인쇄하십시오.$B뒤에수집하는 입력입니다. 이는 이전 예를 기반으로 다음 을 인쇄 sed한다는 것을 의미합니다.P1그런 다음 D이를 삭제하고 다음과 같이 패턴 공간을 스크립트 상단으로 다시 보냅니다.

^2\n3$

...그리고 N스크립트 상단에서 외부 입력 줄을 검색하므로 다음 반복은 다음과 같습니다.

^2\n3\n4$

따라서 5입력에서 첫 번째 항목을 찾으면 패턴 공간은 실제로 다음과 같습니다.

^3\n4\n5$

그런 다음 D선택 루프가 시작되고 완료되면 다음과 같습니다.

^5$

N외부 입력 라인을 당기면 EOF sed가 발생하여 종료됩니다. 그 시점에는 P1행과 2행만 인쇄되었습니다.

실행 예시는 다음과 같습니다.

A=8 B=7 match='[24689]0'
seq 100 |
sed -ne:t -e"/\n.*$match/D" \
    -e'$!N;//D;/'"$match/{" \
            -e"s/\n/&/$A;t" \
            -e'$q;bt' -e\}  \
    -e's/\n/&/'"$B;tP"      \
    -e'$!bt' -e:P  -e'P;D'

인쇄:

1
2
3
4
5
6
7
8
9
10
11
12
29
30
31
32
49
50
51
52
69
70
71
72
99
100

답변3

사용해도 괜찮다면 다음을 사용하세요 vim.

$ export PAT=fff A=1 B=2
$ vim -Nes "+g/${PAT}/.-${B},.+${A}d" '+w !tee' '+q!' foo
aaa
bbb
ccc
hhh
iii
  • -Nes호환되지 않는 자동 Ex 모드를 켭니다. 스크립팅에 유용합니다.
  • +{command}{command}vim에게 이 파일을 실행하라고 지시하세요.
  • g/${PAT}/- 일치하는 모든 줄에 /fff/. 이 방식으로 처리하지 않으려는 정규식 특수 문자가 패턴에 포함되어 있으면 까다로울 수 있습니다.
  • .-${B}- 이 행 위의 1개 행부터 시작
  • .+${A}- 이 줄 아래로 두 줄 이동합니다(참조:he cmdline-ranges모두)
  • d- 행을 삭제합니다.
  • +w !tee그런 다음 표준 출력에 씁니다.
  • +q!변경 사항을 저장하지 않고 종료합니다.

변수를 건너뛰고 패턴과 숫자를 직접 사용할 수 있습니다. 나는 그것들을 명시적인 목적으로만 사용합니다.

답변4

임시 파일을 사용하면 충분한 결과를 얻을 수 있습니다.

my_file=file.txt #or =$1 if in a script

#create a file with all the lines to discard, numbered
grep -n -B1 -A5 TBD "$my_file" |cut -d\  -f1|tr -d ':-'|sort > /tmp/___"$my_file"_unpair

#number all the lines
nl -nln "$my_file"|cut -d\  -f1|tr -d ':-'|sort >  /tmp/___"$my_file"_all

#join the two, creating a file with the numbers of all the lines to keep
#i.e. of those _not_ found in the "unpair" file
join -v2  /tmp/___"$my_file"_unpair /tmp/___"$my_file"_all|sort -n > /tmp/___"$my_file"_lines_to_keep

#eventually use these line numbers to extract lines from the original file
nl -nln $my_file|join - /tmp/___"$my_file"_lines_to_keep |cut -d\  -f2- > "$my_file"_clean

밝혀지다충분하다프로세스에서 일부 들여쓰기가 손실될 수 있지만 XML 또는 들여쓰기를 구분하지 않는 파일인 경우에는 문제가 되지 않습니다. 이 스크립트는 RAM 드라이브를 사용하기 때문에 이러한 임시 파일을 쓰고 읽는 것은 메모리에서 작업하는 것만큼 빠릅니다.

관련 정보