일치 항목과 이전 줄을 제외한 모든 것을 grep하는 방법

일치 항목과 이전 줄을 제외한 모든 것을 grep하는 방법

grep이 일치하지 않게 하려는 텍스트 파일과 패턴이 있습니다. 문제는 이전 행도 일치하지 않기를 원한다는 것입니다.

내 파일:

line 1
line 2
pattern
line 4

나는 그것을 시도했고 cat file | grep -v pattern결과는 다음과 같습니다.

line 1
line 2
line 4

그런 다음 시도해 보았는데 cat file | grep -B 1 pattern결과는 다음과 같습니다.

line 2
pattern

그러나 함께 사용하면 다음과 같은 결과를 cat file | grep -v -B 1 pattern얻습니다.

line 2

출력을 어떻게 만들 수 있습니까?

line 1
line 4

답변1

나는 grep파일에서 한 줄을 추출할 때만 이 도구를 사용하는 경향이 있으므로 텍스트에서 더 복잡한 편집을 수행해야 할 때는 다른 도구를 사용합니다.

여기의 모든 솔루션은 패턴이 텍스트에서 여러 번 나타날 수 있다고 가정하고 패턴이 발생하는 줄과 바로 앞의 줄을 삭제합니다. 처음 두 솔루션은 패턴이 연속된 라인에서 일치하는 경우 문제가 발생합니다.


sed패턴을 일치시키고 /pattern/명령 N및 를 트리거하도록 할 수 있습니다 d. 이 명령은 다음 줄을 버퍼에 추가한 다음 두 가지를 모두 삭제합니다.

sed '/pattern/ { N; d; }' file

왜냐하면 당신은 그 줄을 버리고 싶기 때문입니다앞으로패턴을 일치시키기 위해 데이터를 거꾸로 입력합니다 sed. 마지막 줄에서 시작하여 파일의 시작 부분으로 이동합니다. sed완료되면 데이터를 다시 반전시킵니다.

tac file | sed '/pattern/ { N; d; }' | tac

tac유틸리티는 GNU coreutils의 일부입니다. 대부분의 비 GNU 시스템을 tail -r대신 사용할 수 있습니다 tac( tail(1)설명서를 확인하세요).

패턴이 두 개의 연속 행과 일치하는 경우 첫 번째 행이 삭제되기 때문에 첫 번째 행 이전의 행은 삭제되지 않습니다.


ed편집기를 사용하십시오 :

printf '%s\n' 'g/pattern/ -1,. d' ,p Q | ed -s file

g/pattern/ -1,. d그러면 파일 내용 에 명령이 적용됩니다 . 이 명령은 일치하는 각 줄을 검색한 pattern다음 해당 줄과 그 앞의 줄을 삭제합니다.

최종 ,p편집 Q명령은 전체 파일을 인쇄하고 저장하지 않고 편집기를 종료합니다.

패턴이 두 개의 연속 행과 일치하는 경우 첫 번째 행 앞의 행을 삭제한 후 두 번째 행 앞의 행을 삭제합니다.

(마지막 문장은옳은글을 쓰다 보면 분명 그냥 쓰는 문장인 게 분명하다. )


grep비표준이지만 일반적으로 사용되는 -B옵션을 사용하여 삭제해야 하는 줄 번호를 제공 할 수도 있습니다 . 이 숫자는 sed원시 데이터에서 실행되는 스크립트 로 변환될 수 있습니다 .

grep -n -B1 'pattern' file | sed 's/[:-].*/d/' | sed -f /dev/stdin file

grep질문의 텍스트를 기반으로 명령이 출력됩니다.

2-line 2
3:pattern

...첫 번째 sed명령은 이를 sed편집 명령 으로 변환하고 2d그 다음에는 3d("2행과 3행 삭제")가 이어집니다. sed파이프라인의 마지막 명령은 이 편집 스크립트를 가져와 원본 텍스트에 적용합니다.

이 변형은 먼저 삭제해야 할 모든 줄을 찾은 다음 삭제하는 2단계 접근 방식을 사용하기 때문에 패턴과 일치하는 연속 줄의 문제가 없습니다(텍스트를 처음 읽을 때 줄을 삭제하는 대신).

답변2

tac과 함께 awk를 사용하면 일치하는 패턴 앞의 행을 원하는 만큼 제거할 수 있습니다.

$ tac file | awk '/pattern/{c=2} !(c&&c--)' file | tac
line 2
line 1

삭제하려는 행 수(최대 일치하는 행까지)를 변경하세요. 예를 들어 숫자 97과 그 앞의 94개 행을 삭제 c=2하세요 .c=5

$ seq 100 | tac | awk '/97/{c=95} !(c&&c--)' | tac
1
2
98
99
100

이제 awk 대신 sed를 사용해 보세요 :-).

바라보다인쇄를 위해 sed- 또는 awk-a-line-follow-a-matching-pattern을 사용하십시오.이것과 다른 관련 관용구에 대한 설명입니다.

답변3

노트:file이 코드는 의 출력과 일치하는 각 행에 대해 중복된 행이나 하위 문자열이 없는 경우 에만 작동합니다 grep -B1 pattern file.

예를 들어 file다음 줄을 포함하는 경우:

line 1
line 2
line 2
pattern
line 1 line 2
line 3

그리고 내가 사용하는 출력은 grep -B1 pattern file | grep -v "$(cat)" file여러분이 기대하는 것과 다를 것입니다.

line 1
line 3

이 문제를 해결하는 가장 좋은 방법은 다음을 사용하는 것입니다.코살로난다의 답변

해결책(위에서 설명한 것처럼 중복된 행이나 하위 문자열이 없는 경우에만 작동합니다)

이것은 bash나에게 효과적입니다(더 좋은 방법이 있다고 생각합니다).

grep -B1 pattern file | grep -v "$(cat)" file

zsh위 명령 에는 아무런 영향이 없습니다. 이유는 모르겠습니다. 하지만 다음을 사용할 수 있습니다.

grep -B1 pattern file | { val="$(cat)" ; grep -v "$val" file; }

폴리스티렌cat your_file | grep pattern중복되므로 사용할 필요가 없습니다 . 당신은 사용해야합니다grep pattern your_file

답변4

pcregrepUltiline 모드를 사용할 수 있습니다 M.

pcregrep -Mv '\n.*pattern'

첫 번째 행이 패턴과 일치하면 삭제되지 않습니다. 이 문제는 다음을 사용하여 해결할 수 있습니다.

pcregrep -Mv '(\n)?.*pattern'

( (...)이 부분은 \n분명히 필요한데 버전 8.39에서는 왜 작동하지 않는지 모르겠습니다 \n?.*pattern. )[\n]?.*pattern

관련 정보