grep을 사용하여 여러 줄에서 패턴을 찾는 방법은 무엇입니까?


208

"abc"및 "efg"가 순서대로있는 파일을 찾고 싶습니다.이 두 문자열은 해당 파일에서 다른 줄에 있습니다. 예 : 내용이 담긴 파일 :

blah blah..
blah blah..
blah abc blah
blah blah..
blah blah..
blah blah..
blah efg blah blah
blah blah..
blah blah..

일치해야합니다.


답변:


225

이 작업에는 Grep이 충분하지 않습니다.

대부분의 최신 Linux 시스템에서 발견되는 pcregrep 은 다음과 같이 사용할 수 있습니다.

pcregrep -M  'abc.*(\n|.)*efg' test.txt

여기서 -M, --multiline 패턴이 한 줄보다 더 일치시킬 수 있습니다

최신 pcre2grep 도 있습니다. 둘 다 PCRE 프로젝트에 의해 제공됩니다 .

pcre2grep는 포트 의 일부로 Mac 포트 를 통해 Mac OS X에서 사용할 수 있습니다 pcre2.

% sudo port install pcre2 

다음과 같이 Homebrew 를 통해 :

% brew install pcre

또는 pcre2

% brew install pcre2

pcre2grep는 Linux (Ubuntu 18.04+) 에서도 사용 가능 합니다

$ sudo apt install pcre2-utils # PCRE2
$ sudo apt install pcregrep    # Older PCRE

11
@StevenLu- -M, --multiline패턴이 두 줄 이상 일치하도록 허용합니다.
링 무기명

7
. * (\ n |.) *는 (\ n |.) *와 같으며 후자는 더 짧습니다. 또한 내 시스템에서 더 긴 버전을 실행하면 "pcre_exec () error -8"이 발생합니다. 따라서 대신 'abc (\ n |.) * efg'를 시도하십시오!
daveagp

6
이 경우 예를 들어 욕심없는 표현을 만들어야합니다.'abc.*(\n|.)*?efg'
ring bearer

4
당신은 먼저를 생략 할 수 있습니다 .*> - 'abc(\n|.)*?efg'정규 표현식 단축하기 위해 (그리고 현학적 수)
미치

6
pcregrep일을 더 쉽게 만들어 주지만 grep작동합니다. 예를 들어 stackoverflow.com/a/7167115/123695
Michael Mior

113

grep이 가능한지 확실하지 않지만 sed는 매우 쉽습니다.

sed -e '/abc/,/efg/!d' [file-with-content]

4
이것은 파일을 찾지 못하고 단일 파일에서 일치하는 부분을 반환합니다
shiggity

11
@Lj. 이 명령을 설명해 주시겠습니까? 나는에 익숙 sed하지만 이전에 그런 표현을 본 적이 없다면.
Anthony

1
@Anthony, 그것은 sed의 맨 페이지에 주소 아래 문서화되어 있습니다. / abc / & / efg /는 주소임을 인식하는 것이 중요합니다.
오징어

49
이 답변이 조금 더 설명이 도움이된다면 도움이 될 것이라고 생각합니다.이 경우 한 번 더 투표했습니다. 나는 약간의 sed를 알고 있지만, 30 분의 말다툼 후 의미있는 종료 코드를 생성하기 위해이 답변을 사용하기에는 충분하지 않습니다. 팁 : 'RTFM'은 이전 의견에서 볼 수 있듯이 StackOverflow에서 투표를하는 경우가 거의 없습니다.
Michael Scheper 2016 년

25
예를 들어 빠른 설명 : sed '1,5d': 1과 5 사이의 줄을 삭제하십시오. sed '1,5! d': 1과 5 사이에없는 줄을 삭제하십시오 (즉, 줄을 유지하십시오). / pattern /으로 줄을 검색하십시오. sed -n '/ abc /, / efg /
p'p

86

이 답변에서 영감을 얻은 솔루션은 다음과 같습니다 .

  • 'abc'와 'efg'가 같은 줄에있을 수있는 경우 :

    grep -zl 'abc.*efg' <your list of files>
  • 'abc'와 'efg'가 다른 행에 있어야하는 경우 :

    grep -Pzl '(?s)abc.*\n.*efg' <your list of files>

매개 변수 :

  • -z입력을 행 세트로 취급하십시오. 각 행은 개행 대신 0 바이트로 종료됩니다. 즉, grep은 입력을 하나의 큰 행으로 취급합니다.

  • -l 출력이 정상적으로 인쇄 될 각 입력 파일의 인쇄 이름.

  • (?s)PCRE_DOTALL을 활성화하십시오. 즉, '.' 모든 문자 또는 개행을 찾습니다.


@syntaxerror 아니오, 나는 단지 소문자라고 생각합니다 l. AFAIK 번호 -1옵션 이 없습니다 .
Sparhawk

결국 당신이 옳은 것 같습니다, 아마도 테스트 할 때 오타가 있었을 것입니다. 어쨌든 거짓된 길을 놓아서 죄송합니다.
syntaxerror

6
이것은 우수하다. 이것에 관한 질문이 하나 있습니다. -z옵션이 개행을 처리하기 위해 grep을 지정 하면 zero byte characters(?s)정규 표현식에 개행이 필요 합니까? 이미 개행 문자가 아닌 .경우 직접 일치시킬 수 없습니까?
Durga Swaroop

1
-z (일명 --null-data) 및 (? s)는 여러 줄을 표준 grep과 일치시키기 위해 필요한 것입니다. MacOS 사용자는 시스템에서 -z 또는 --null-data 옵션의 가용성에 대한 의견을 남겨주십시오!
Zeke Fast

4
-z MacOS에서는 사용할 수 없음
Dylan Nicholson

33

포스터 LJ가 위에서 언급했듯이 sed로 충분합니다.

! d 대신 p를 사용하여 간단히 인쇄 할 수 있습니다.

sed -n '/abc/,/efg/p' file

16

pcregrep에 크게 의존했지만 최신 grep을 사용하면 많은 기능을 위해 pcregrep을 설치할 필요가 없습니다. 그냥 사용하십시오 grep -P.

OP 질문의 예에서 다음 옵션이 잘 작동한다고 생각합니다. 두 번째는 질문을 이해하는 방법과 가장 잘 일치합니다.

grep -Pzo "abc(.|\n)*efg" /tmp/tes*
grep -Pzl "abc(.|\n)*efg" /tmp/tes*

텍스트를 / tmp / test1로 복사하고 'g'를 삭제하고 / tmp / test2로 저장했습니다. 다음은 첫 번째가 일치하는 문자열을 표시하고 두 번째는 파일 이름 만 표시 함을 보여주는 출력입니다 (일반적으로 -o는 일치를 표시하고 일반적인 -l은 파일 이름 만 표시). 'z'는 여러 줄에 필요하며 '(. | \ n)'은 'newline'이외의 다른 것 또는 'newline'과 일치한다는 것을 의미합니다.

user@host:~$ grep -Pzo "abc(.|\n)*efg" /tmp/tes*
/tmp/test1:abc blah
blah blah..
blah blah..
blah blah..
blah efg
user@host:~$ grep -Pzl "abc(.|\n)*efg" /tmp/tes*
/tmp/test1

버전이 충분히 새 버전인지 확인하려면 실행 man grep하고 이와 비슷한 것이 맨 위 근처에 나타나는지 확인하십시오.

   -P, --perl-regexp
          Interpret  PATTERN  as a Perl regular expression (PCRE, see
          below).  This is highly experimental and grep -P may warn of
          unimplemented features.

그것은 GNU grep 2.10에서 온 것입니다.


14

tr개행을 다른 문자로 바꾸려면 먼저 사용하여 쉽게 수행 할 수 있습니다 .

tr '\n' '\a' | grep -o 'abc.*def' | tr '\a' '\n'

여기서는 \a개행 문자 대신 알람 문자 (ASCII 7)를 사용하고 있습니다. 이것은 거의 텍스트에서 찾을 수 없으며로 grep일치 시키 .거나 구체적으로 일치 시킬 수 있습니다 \a.


1
이것은 나의 접근 방식이지만 사용 \0하고 있었고 필요 grep -a하고 일치했습니다 \x00... 당신은 나를 단순화하는 데 도움이되었습니다! echo $log | tr '\n' '\0' | grep -aoE "Error: .*?\x00Installing .*? has failed\!" | tr '\0' '\n'은 지금echo $log | tr '\n' '\a' | grep -oE "Error: .*?\aInstalling .*? has failed\!" | tr '\a' '\n'
Charlie Gorichanaz

1
사용하십시오 grep -o.
kyb

7

awk one-liner :

awk '/abc/,/efg/' [file-with-content]

4
abc파일에 엔딩 패턴이 없거나 마지막 엔딩 패턴이없는 경우 파일의 끝에서 끝까지 행복하게 인쇄됩니다 . 당신은 그것을 고칠 수는 있지만 스크립트를 상당히 복잡하게 만듭니다.
tripleee

/efg/출력 에서 제외하는 방법 ?
kyb

6

Perl을 사용하면 아주 쉽게 할 수 있습니다.

perl -ne 'if (/abc/) { $abc = 1; next }; print "Found in $ARGV\n" if ($abc && /efg/); }' yourfilename.txt

단일 정규 표현식으로도 그렇게 할 수 있지만 파일의 전체 내용을 단일 문자열로 가져와 큰 파일로 너무 많은 메모리를 차지할 수 있습니다. 완전성을 위해 다음은 그 방법입니다.

perl -e '@lines = <>; $content = join("", @lines); print "Found in $ARGV\n" if ($content =~ /abc.*efg/s);' yourfilename.txt

발견 된 두 번째 대답은 몇 줄에 일치하는 전체 여러 줄 블록을 추출하는 데 유용했습니다 .*?. 최소한의 일치를 얻으려면 욕심없는 일치 ( )를 사용해야했습니다 .
RichVel

5

나는 grep으로 어떻게 할 것인지 모르겠지만 awk로 이런 식으로 할 것입니다.

awk '/abc/{ln1=NR} /efg/{ln2=NR} END{if(ln1 && ln2 && ln1 < ln2){print "found"}else{print "not found"}}' foo

하지만 어떻게해야하는지주의해야합니다. 정규식이 하위 문자열 또는 전체 단어와 일치하도록 하시겠습니까? 적절하게 \ w 태그를 추가하십시오. 또한 이것은 예제를 언급 한 방식과 엄격하게 일치하지만 abc가 efg 후 두 번째로 나타날 때 제대로 작동하지 않습니다. 이를 처리하려면 / abc / case 등에 적절한 if를 추가하십시오.


3

슬프게도, 당신은 할 수 없습니다. 로부터 grep문서 :

grep 은 지정된 PATTERN과 일치하는 을 찾기 위해 명명 된 입력 FILE (또는 파일이 명명되지 않은 경우 또는 단일 하이픈-마이너스 (-)가 파일 이름으로 제공되는 경우)의 표준 입력을 검색합니다.


어떻 grep -Pz
습니까

3

컨텍스트를 기꺼이 사용하려는 경우 다음을 입력하여 얻을 수 있습니다.

grep -A 500 abc test.txt | grep -B 500 efg

"abc"와 "efg" 사이의 모든 항목 이 서로 500 줄 이내에있는 한 표시됩니다.


3

두 단어가 서로 가까이 있어야하는 경우 (예 : 3 줄 이하) 다음을 수행 할 수 있습니다.

find . -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"

동일한 예제이지만 * .txt 파일 만 필터링 :

find . -name *.txt -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"

또한 정규식으로 찾으려면 grep명령을 egrep명령으로 바꿀 수도 있습니다 .


3

며칠 전에 여러 줄 일치 또는 조건을 사용하여 직접 지원하는 grep 대안을 발표했습니다. 이 예제의 명령은 다음과 같습니다.

여러 줄 :

sift -lm 'abc.*efg' testfile

정황:

sift -l 'abc' testfile --followed-by 'efg'

'efg'가 특정 수의 행 내에서 'abc'를 따라야한다고 지정할 수도 있습니다.

sift -l 'abc' testfile --followed-within 5:'efg'

sift-tool.org 에 대한 자세한 정보를 찾을 수 있습니다 .


필자는 첫 번째 예제가 sift -lm 'abc.*efg' testfile효과 가 없다고 생각 합니다. 왜냐하면 일치하는 것은 탐욕스럽고 efg파일 의 마지막까지 모든 줄을 모으기 때문 입니다.
Dr. Alex RE

2

sed 옵션이 가장 간단하고 쉬운 반면, LJ의 한 줄짜리 라이너는 가장 휴대하기 쉽지 않습니다. C 쉘 버전을 고수 한 사람들은 앞머리를 피해야합니다.

sed -e '/abc/,/efg/\!d' [file]

불행히도 bash et al.에서는 작동하지 않습니다.


1
#!/bin/bash
shopt -s nullglob
for file in *
do
 r=$(awk '/abc/{f=1}/efg/{g=1;exit}END{print g&&f ?1:0}' file)
 if [ "$r" -eq 1 ];then
   echo "Found pattern in $file"
 else
   echo "not found"
 fi
done

1

패턴 순서에 관심이없는 경우 grep을 사용할 수 있습니다.

grep -l "pattern1" filepattern*.* | xargs grep "pattern2"

grep -l "vector" *.cpp | xargs grep "map"

grep -l첫 번째 패턴과 일치하는 모든 파일을 찾고 xargs는 두 번째 패턴에 대해 grep합니다. 도움이 되었기를 바랍니다.


1
"pattern1"과 "pattern2"순서는 파일에 나타나는 순서를 무시하지만 OP는 "pattern1"이후에 "pattern2"가 나타나는 파일 만 일치하도록 지정합니다.
Emil Lundberg '

1

실버 수색자 :

ag 'abc.*(\n|.)*efg'

링 베어러의 대답과 비슷하지만 대신 ag를 사용하십시오. 은색 검색기의 속도 이점이 여기에서 빛날 수 있습니다.


1
이것은 작동하지 않는 것 같습니다. (echo abctest; echo efg)|ag 'abc.*(\n|.)*efg'일치하지 않음
phiresky

1

grep에 -P 옵션을 사용하여 다중 fasta 파일에서 fasta 시퀀스를 추출하기 위해 이것을 사용했습니다.

grep -Pzo ">tig00000034[^>]+"  file.fasta > desired_sequence.fasta
  • 펄 기반 검색의 경우 P
  • 줄 바꿈 문자가 아닌 0 바이트로 줄 끝을 만들기위한 z
  • grep이 전체 행을 리턴 한 이후에 일치하는 것을 캡처하려면 (이 경우 -z를 수행 한 이후 전체 파일)

정규 표현식의 핵심은 [^>]"심볼 이하"로 번역되는 것입니다.


0

Balu 모한의 대답에 대한 대안으로,이 패턴의 순서 만 사용하여 적용 할 수 있습니다 grep, head그리고 tail:

for f in FILEGLOB; do tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep "pattern2" &>/dev/null && echo $f; done

그러나 이것은 매우 예쁘지 않습니다. 보다 읽기 쉬운 형식 :

for f in FILEGLOB; do
    tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null \
    | grep -q "pattern2" \
    && echo $f
done

이것은 모든 파일의 이름이 인쇄됩니다 "pattern2"후에 나타납니다 "pattern1", 또는 어디에 둘 다 같은 줄에 표시를 :

$ echo "abc
def" > a.txt
$ echo "def
abc" > b.txt
$ echo "abcdef" > c.txt; echo "defabc" > d.txt
$ for f in *.txt; do tail $f -n +$(grep -n "abc" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep -q "def" && echo $f; done
a.txt
c.txt
d.txt

설명

  • tail -n +i- i일을 포함 하여 모든 줄을 인쇄하십시오.
  • grep -n -줄 번호와 일치하는 줄 앞에 추가
  • head -n1 -첫 번째 행만 인쇄
  • cut -d : -f 1- :구분자로 사용하여 첫 번째 절단 열을 인쇄
  • 2>/dev/null- 표현식이 비어있는 tail경우 발생하는 무음 오류 출력$()
  • grep -q- grep종료 코드에만 관심이 있기 때문에 일치하는 것이 발견되면 즉시 침묵 하고 반환

누구든지 설명해 주 &>시겠습니까? 나는 그것을 사용하고 있지만 어디서나 문서화 된 것을 본 적이 없다. BTW, 왜 그런 식으로 grep을 침묵시켜야합니까? grep -q트릭도하지 않습니까?
syntaxerror

1
&>bash가 표준 출력과 표준 오류를 리디렉션하도록 지시합니다. bash 매뉴얼의 REDIRECTION을 참조하십시오. 당신은 우리가 잘 잡을 수있는 grep -q ...대신에 할 수 있다는 점에서 매우 옳습니다 grep ... &>/dev/null!
Emil Lundberg

그렇게 생각. 많은 어색한 추가 타이핑의 고통을 없애줍니다. 설명 주셔서 감사합니다-매뉴얼에서 약간 건너 뛰어야합니다. (몇 시간 전에 원격으로 관련된 것을 찾았습니다.) --- 대답에서 변경하는 것을 고려할 수도 있습니다. :)
syntaxerror

0

이것도 작동해야합니까?!

perl -lpne 'print $ARGV if /abc.*?efg/s' file_list

$ARGVfile_list /s개행 에서 수정 자 검색을 읽을 때 현재 파일의 이름을 포함합니다 .


0

파일 패턴 *.sh은 디렉토리가 검사되지 않도록하는 데 중요합니다. 물론 일부 테스트는이를 방지 할 수 있습니다.

for f in *.sh
do
  a=$( grep -n -m1 abc $f )
  test -n "${a}" && z=$( grep -n efg $f | tail -n 1) || continue 
  (( ((${z/:*/}-${a/:*/})) > 0 )) && echo $f
done

그만큼

grep -n -m1 abc $f 

최대 1 개의 일치 항목을 검색하고 행 번호를 반환합니다 (-n). 일치하는 것이 발견되면 (test -n ...) efg의 마지막 일치 (모두 찾기 및 tail -n 1로 마지막 일치)를 찾으십시오.

z=$( grep -n efg $f | tail -n 1)

그렇지 않으면 계속하십시오.

결과는 18:foofile.sh String alf="abc";":"에서 줄 끝까지 잘라 내야합니다.

((${z/:*/}-${a/:*/}))

두 번째 표현식의 마지막 일치가 첫 번째 일치의 첫 번째 일치를 지나면 양수 결과를 반환해야합니다.

그런 다음 filename을보고합니다 echo $f.


0

왜 간단하지 않은가?

egrep -o 'abc|efg' $file | grep -A1 abc | grep efg | wc -l

0 또는 양의 정수를 반환합니다.

egrep -o (일치 만 표시, 트릭 : 동일한 줄의 여러 일치 항목이 다른 줄에있는 것처럼 여러 줄 출력을 생성 함)

  • grep -A1 abc (ABC와 그 뒤에 줄을 인쇄하십시오)

  • grep efg | wc -l (동일한 또는 다음 줄에서 abc 이후에 발견 된 0-n 개의 efg 줄, 결과는 'if'에 사용될 수 있습니다)

  • 패턴 일치가 필요한 경우 grep을 egrep 등으로 변경할 수 있습니다.


0

찾고있는 두 문자열 'abc'와 'efg'사이의 거리에 대한 추정이 있다면 다음을 사용할 수 있습니다.

grep -r . -e 'abc' -A num1 -B num2 | grep 'efg'

이렇게하면 첫 번째 grep은 'abc'와 그 뒤에 # num1 행, 그 뒤에 # num2 행이있는 행을 반환하고 두 번째 grep은 'efg'를 얻기 위해 모든 것을 거칩니다. 그러면 어떤 파일이 함께 나타나는지 알게됩니다.


0

ugrep 몇 달 전에 발표 :

ugrep 'abc(\n|.)+?efg'

이 도구는 속도에 최적화되어 있습니다. 또한 GNU / BSD / PCRE-grep과 호환됩니다.

파일 의 마지막까지 +?모든 행을 efg함께 일치시키지 않으려면 지연 반복을 사용해야 efg합니다.


-3

이것은 작동해야합니다 :

cat FILE | egrep 'abc|efg'

일치하는 항목이 둘 이상인 경우 grep -v를 사용하여 필터링 할 수 있습니다.


2
이 코드 스 니펫은 환영하고 도움이 될 수 있지만 이것이 문제를 해결하는 방법이유에 대한 설명포함하면 크게 향상 될 것 입니다. 지금 질문하는 사람뿐만 아니라 앞으로 독자들에게 질문에 대답하고 있음을 기억하십시오! 제발 편집 설명을 추가하고, 제한 및 가정이 적용 무엇의 표시를 제공하는 답변을.
Toby Speight

1
질문에 명시된 것처럼 실제로 여러 줄을 검색하지는 않습니다 .
n.st
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.