여러 줄에서 패턴을 어떻게 "그리프"할 수 있습니까?


24

내가 잘못 사용하고 것 grep/을 egrep.

여러 줄에서 문자열을 검색하려고했지만 찾고있는 것이 일치해야한다는 것을 알면서 일치를 찾을 수 없습니다. 원래 나는 내 정규 표현식이 잘못되었다고 생각했지만 결국이 도구는 한 줄에 따라 작동한다는 것을 읽었습니다 (또한 내 정규 표현식은 너무 사소하여 문제가되지 않았습니다).

그렇다면 어떤 도구를 사용하여 여러 줄의 패턴을 검색 할 수 있습니까?



1
@ CiroSantilli-이 Q와 연결된 Q가 중복이라고 생각하지 않습니다. 다른 Q는 멀티 라인 패턴 일치를 수행하는 방법 (즉, 어떤 도구를 사용해야합니까 / 사용할 수 있는지)을 묻는 반면이 질문을 수행하는 방법을 묻습니다 grep. 그들은 밀접하게 관련되어 있지만 바보는 아닙니다.
slm

@sim은 이러한 경우를 결정하기가 어렵습니다. 요점을 알 수 있습니다. 나는 사용자가 "grep""그렙"이라는 동사를 제안 한다고 말했기 때문에이 특별한 경우가 복제본으로 더 낫다고 생각 하며, 수락 된 것을 포함하여 최고 답변은 grep을 사용하지 않습니다.
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

답변:


24

다음 sedgrep여러 줄에 걸쳐 같은 동작을 제공하는 것입니다 .

sed -n '/foo/{:start /bar/!{N;b start};/your_regex/p}' your_file

작동 원리

  • -n 모든 줄을 인쇄하는 기본 동작을 억제합니다
  • /foo/{}foo일치하는 라인과 스퀴지 내부의 내용 을 일치 시키고 수행 하도록 지시합니다 . foo패턴의 시작 부분으로 교체하십시오 .
  • :start 는 정규식이 끝날 때까지 계속 반복되는 데 도움이되는 분기 레이블입니다.
  • /bar/!{}일치하지 않는 행에 대한 구불 구불 한 내용을 실행합니다 bar. bar패턴의 끝 부분으로 교체하십시오 .
  • N다음 행을 활성 버퍼에 추가합니다 ( sed패턴 공간이라고 함).
  • b startstart패턴 공간에 포함되지 않는 한 다음 줄을 계속 추가하기 위해 이전에 만든 레이블로 무조건 분기 합니다 bar.
  • /your_regex/p패턴 공간이 일치하면 인쇄합니다 your_regex. your_regex여러 줄에서 일치시키려는 전체 식으로 바꿔야 합니다.

1
+1 이것을 툴 릭트에 추가! 감사.
wmorrison365

참고 : MacOS의 경우sed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
Stan James

1
가져 오기 sed: unterminated {오류
Nomaed

@Nomaed Shot은 어두운 곳이지만 정규 표현식에 "{"문자가 포함되어 있습니까? 그렇다면 백 슬래시를 이스케이프 처리해야합니다.
Joseph R.

1
@Nomaed 구현 간의 차이점 과 관련이있는 것 같습니다 sed. 위의 스크립트를 표준 호환으로 만들기 위해 해당 답변의 권장 사항을 따르려고했지만 "시작"이 정의되지 않은 레이블이라고 나에게 말했습니다. 따라서 이것이 표준 호환 방식으로 수행 될 수 있는지 확실하지 않습니다. 당신이 그것을 관리한다면, 자유롭게 답변을 편집하십시오.
조셉 알

19

나는 일반적으로 또는을 pcregrep사용하여 대부분의 리눅스 버전에 설치할 수 있는 도구를 사용합니다 .yumapt

예를 들어.

testfilecontent 라는 이름의 파일이 있다고 가정하십시오

abc blah
blah blah
def blah
blah blah

다음 명령을 실행할 수 있습니다.

$ pcregrep -M  'abc.*(\n|.)*def' testfile

여러 줄에 걸쳐 패턴 일치를 수행합니다.

또한 동일한 작업을 수행 할 수 sed있습니다.

$ sed -e '/abc/,/def/!d' testfile

5

Perl을 사용하는 더 간단한 방법은 다음과 같습니다.

perl -e '$f=join("",<>); print $& if $f=~/foo\nbar.*\n/m' file

또는 (JosephR 이후 했다 sed경로를 , 나는 뻔뻔스럽게 그의 훔칠 수 있습니다 제안 )

perl -n000e 'print $& while /^foo.*\nbar.*\n/mg' file

설명

$f=join("",<>);: 전체 파일을 읽고 내용 (개행 및 모두)을 변수에 저장 $f합니다. 그런 다음 match을 시도하고 일치 foo\nbar.*\n하면 인쇄합니다 (특수 변수 $&는 마지막으로 찾은 일치를 보유합니다). 은 ///m줄 바꿈에 걸쳐 정규 표현식 매치를 만들기 위해 필요합니다.

-0입력 레코드 구분자를 설정한다. 이것을 설정하면 00'문단 모드' 가 활성화됩니다. 여기서 Perl은 연속 줄 바꿈 ( \n\n)을 레코드 구분 기호로 사용합니다. 연속적인 줄 바꿈이없는 경우 전체 파일을 한 번에 읽습니다 (슬러그).

경고:

되지 대용량 파일에 대해이 작업을 수행, 그것은 전체 파일을 메모리로로드하고 문제가 될 수 있습니다.


2

이를 수행하는 한 가지 방법은 Perl을 사용하는 것입니다. 예를 들어 파일 이름은 foo다음과 같습니다.

foo line 1
bar line 2
foo
foo
foo line 5
foo
bar line 6

이제 foo로 시작하는 줄과 bar로 시작하는 줄을 비교하는 Perl이 있습니다.

cat foo | perl -e 'while(<>){$all .= $_}
  while($all =~ /^(foo[^\n]*\nbar[^\n]*\n)/m) {
  print $1; $all =~ s/^(foo[^\n]*\nbar[^\n]*\n)//m;
}'

펄, 세분화 :

  • while(<>){$all .= $_} 전체 표준 입력을 변수에로드합니다. $all
  • while($all =~변수 all에는 정규 표현식이 있지만 ...
  • /^(foo[^\n]*\nbar[^\n]*\n)/m정규 표현식 : 줄의 시작 부분에 foo, 줄 바꿈이 아닌 문자 수, 줄 바꿈, 바로 뒤에 "bar"가 있고 나머지 줄에는 줄이 있습니다. /m정규식의 끝에 "여러 줄에 걸쳐 일치"를 의미
  • print $1 괄호 안에있는 정규식의 일부를 인쇄하십시오 (이 경우 전체 정규식).
  • s/^(foo[^\n]*\nbar[^\n]*\n)//m 정규식에 대한 첫 번째 일치를 지우므로 문제의 파일에서 여러 개의 정규식을 일치시킬 수 있습니다

그리고 출력 :

foo line 1
bar line 2
foo
bar line 6

3
당신의 Perl이 더 관용적으로 단축 될 수 있다고 말하기 위해 방금 들었습니다.perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo
Joseph R.

2

grep alternative sift 는 여러 줄 일치를 지원합니다 (면책 조항 : 저자입니다).

다음을 testfile포함 한다고 가정하십시오 .

<도서>
  <title> 로렘 입숨 </ title>
  <설명> Lorem ipsum dolor 앉은 자세, 헌신
  지각 제거, sed do eiusmod tempor incididunt ut
  Labore et dolore magna aliqua </ description>
</ book>


sift -m '<description>.*?</description>' (설명을 포함하는 줄을 보여주십시오)

결과:

테스트 파일 : <설명> Lorem ipsum dolor 앉은 자세, 헌신
테스트 파일 : 제거 엘리트, sed do eiusmod tempor incididunt ut
테스트 파일 : labore et dolore magna aliqua </ description>


sift -m '<description>(.*?)</description>' --replace 'description="$1"' --no-filename (설명을 추출하고 다시 포맷하십시오)

결과:

description = "Lorem ipsum dolor 앉은 자세, 헌신
  지각 제거, sed do eiusmod tempor incididunt ut
  Labore et dolore magna aliqua "

1
아주 좋은 도구입니다. 축하합니다! 우분투와 같은 배포판에 포함 시키십시오.
Lourenco

2

Perl-regexp매개 변수 를 지원하는 일반적인 grep P이이 작업을 수행합니다.

$ echo 'abc blah
blah blah
def blah
blah blah' | grep -oPz  '(?s)abc.*?def'
abc blah
blah blah
def

(?s) 정규 표현식의 점을 문자뿐만 아니라 줄 바꿈과 일치하도록 DOTALL 수정 자라고합니다.


나는이 솔루션을하려고하면 출력은 'DEF'에 종료되지만 파일 'ㅋ'의 끝으로 이동하지 않습니다
버클리

아마 당신의 grep이 -P옵션을 지원하지 않을 수도 있습니다
Avinash Raj

1

다른 grep과 함께 grep 및 -A 옵션을 사용 하여이 문제를 해결했습니다.

grep first_line_word -A 1 testfile | grep second_line_word

-A 1 옵션은 찾은 행 다음에 한 행을 인쇄합니다. 물론 파일과 단어 조합에 따라 다릅니다. 그러나 나를 위해 가장 빠르고 안정적인 솔루션이었습니다.


alias grepp = 'grep --color = auto -B10 -A20 -i'다음 cat somefile | 그렙 블라 | grepp foo | grepp bar ... 그렇습니다 -A와 -B는 매우 편리합니다 ... 당신은 최고의 답변을
얻었습니다

1

test.txt 파일에 다음이 포함되어 있다고 가정하십시오 .

blabla
blabla
foo
here
is the
text
to keep between the 2 patterns
bar
blabla
blabla

다음 코드를 사용할 수 있습니다 :

sed -n '/foo/,/bar/p' test.txt

다음 출력의 경우 :

foo
here
is the
text
to keep between the 2 patterns
bar

1

우리가 스스로를 제외하고 두 패턴 사이에 텍스트를 얻으려면.

test.txt 파일에 다음이 포함되어 있다고 가정하십시오 .

blabla
blabla
foo
here
is the
text
to keep between the 2 patterns
bar
blabla
blabla

다음 코드를 사용할 수 있습니다 :

 sed -n '/foo/{
 n
 b gotoloop
 :loop
 N
 :gotoloop
 /bar/!{
 h
 b loop
 }
 /bar/{
 g
 p
 }
 }' test.txt

다음 출력의 경우 :

here
is the
text
to keep between the 2 patterns

어떻게 작동합니까, 단계별로 만들어 봅시다

  1. /foo/{ 행에 "foo"가 포함되면 트리거됩니다.
  2. n 패턴 공간을 다음 줄로 바꿉니다. 즉 "here"라는 단어
  3. b gotoloop "gotoloop"레이블로 분기
  4. :gotoloop "gotoloop"레이블을 정의합니다
  5. /bar/!{ 패턴에 "bar"가 포함되어 있지 않은 경우
  6. h 보류 공간을 패턴으로 대체하므로 "여기"가 보류 공간에 저장됩니다.
  7. b loop "loop"레이블로 분기
  8. :loop "loop"라벨을 정의합니다
  9. N 홀드 공간에 패턴을 추가합니다.
    이제 보유 공간에 다음이 포함됩니다.
    "여기"
    "는"
  10. :gotoloop 우리는 이제 4 단계에 있으며, 줄에 "bar"가 포함될 때까지 반복합니다
  11. /bar/ 루프가 완료되고 "바"가 발견되었습니다. 패턴 공간입니다.
  12. g 패턴 공간은 메인 루프 동안 저장된 "foo"와 "bar"사이의 모든 행을 포함하는 보류 공간으로 대체됩니다.
  13. p 패턴 공간을 표준 출력으로 복사

완료!


잘 했어, +1 나는 보통 개행을 SOH로 자르고 정상적인 sed 명령을 수행 한 다음 개행을 대체하여 이러한 명령을 사용하지 않습니다.
A.Danischewski
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.