grep이 10 줄 내에 표시되지 않는“Foo”인스턴스를 찾습니다.


10

"Foo"가 발생하는 모든 CPP 파일에 대해 전체 트리를 검색하려고한다고 가정하십시오. 나는 할 수있다 :

find . -name "*.cpp" | xargs grep "Foo"

이제 다른 문자열, 예를 들어 "Bar"가 이전 결과의 3 줄 내에 나타나지 않는 인스턴스 나열하려고한다고 가정하십시오 .

따라서 두 개의 파일이 주어졌습니다.

a.cpp

1 Foo
2 qwerty
3 qwerty

b.cpp

1 Foo
2 Bar
3 qwerty

a.cpp의 "Foo"가 있지만 b.cpp의 "Foo"가없는 간단한 검색을 구성하고 싶습니다.

상당히 간단한 방법으로 이것을 달성 할 수있는 방법이 있습니까?


아마도 솔루션은 옵션 grep -A 및 / 또는 grep -B 및 / 또는 grep -C에있을 수 있습니다. 나는 시도하지만 성공과 함께 .... 해요
maurelio79

@ maurelio79 : 내 현재 이론은 이것입니다. 문맥에 -A 10을 사용하여 "Foo"에 대한 Grep. grep -v Bar로 파이프하십시오. 파일 이름 및 줄 번호를 얻기 위해 sed를 파이프하십시오. 그 줄을 인쇄하려면 (뭔가?)로 파이프하십시오.
John

답변:


17

pcregrep:

pcregrep --include='\.cpp$' -rnM 'Foo(?!(?:.*\n){0,2}.*Bar)' .

이 키는 -M고유 한 옵션에 있으며 pcregrep여러 줄을 일치시키는 데 사용됩니다 ( pcregrepRE를 걸을 때 필요에 따라 입력 파일에서 더 많은 데이터를 가져옵니다).

(?!...)perl / PCRE 네거티브 미리보기 RE 연산자입니다. Foo(?!...)일치하는 Foo만큼 ...다음과 일치하지 않습니다.

...(?:.*\n){0,2}.*Bar( .포함하는 행 하였다 0-2 선에서이고, 개행 문자에 일치하지 않음) Bar.


+1 : 훌륭합니다. 정말 고마워; 올바른 정규 표현식을 찾는 것이 쉽지 않다고 확신합니다. 당신의 노력에 대단히 감사합니다. 이것은 내가 원하는대로 정확하게 작동하는 것 같습니다.
John Dibling

2
답변이 필요한 경우 부수적 인 질문입니다. 어떻게 알게 pcregrep되었습니까? 전에는 들어 본 적이 없습니다.
John Dibling

@JohnDibling, 나는 최근 에 unix.SE에서 개인적으로 발견했습니다 . 이 RE는 특히 (?!...)네거티브 미리보기 perlRE 연산자에 익숙 할 때 특히 복잡하지 않습니다 .
Stéphane Chazelas

9

@StephaneChazelas가 제안한pcregrep 대로 사용하면 됩니다 .


이것은 작동해야합니다 :

$ find . -name "*.cpp" | 
    while IFS= read -r file; do 
      grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; 
    done 

아이디어는 grep의 -A스위치 를 사용 하여 일치하는 줄과 N 다음 줄을 출력하는 것입니다. 그런 다음 a를 통해 결과를 전달하고 grep Bar일치하지 않으면 (종료> 0) 파일 이름을 에코합니다.

빈 파일 이름 (공백, 줄 바꿈 또는 기타 이상한 문자가 없음)을 알고있는 경우 다음과 같이 단순화 할 수 있습니다.

$ for file in $(find . -name "*.cpp"); do 
   grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; 
  done 

예를 들면 다음과 같습니다.

terdon@oregano foo $ cat a.cpp 
1 Foo
2 qwerty
3 qwerty
terdon@oregano foo $ cat b.cpp 
1 Foo
2 Bar
3 qwerty
terdon@oregano foo $ cat c.cpp 
1 Foo
2 qwerty
3 qwerty
4 qwerty
5. Bar
terdon@oregano foo $ for file in $(find . -name "*.cpp"); do grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; done 
./c.cpp
./a.cpp

참고 c.cpp포함에도 불구하고 반환되는 Bar행이 함께 있기 때문에 Bar3 개 이상의 행 다음입니다 Foo. 전달 된 값을 변경하여 검색하려는 행 수를 제어합니다 -A.

$ for file in $(find . -name "*.cpp"); do 
   grep -A 10 Foo "$file" | grep -q Bar || echo "$file"; 
  done 
./a.cpp

다음은 더 짧은 것입니다 (을 사용한다고 가정 bash).

$ shopt -s globstar 
$ for file in **/*cpp; do 
    grep -A 10 Foo "$file" | grep -q Bar || echo "$file"; 
  done

중대한

주석에서 Stephane Chazelas가 지적했듯이 위의 솔루션은 전혀 포함되지 않은 파일도 인쇄합니다 Foo. 이것은 이것을 피합니다 :

for file in **/*cpp; do 
  grep -qm 1 Foo "$file" && 
  (grep -A 3 Foo "$file" | grep -q Bar || echo "$file"); 
done

깔끔한 +1. 내가 기대했던 것보다 조금 더 복잡하지만 전혀 나쁘지는 않습니다.
John Dibling

"Foo"는 한 번만 발생한다고 가정합니다. 포함되지 않은 파일도보고합니다 Foo. 따옴표가 누락되었습니다.
Stéphane Chazelas

@ StephaneChazelas 감사합니다, 따옴표가 고정되었습니다. 당신은 파일을보고하지 않는 것에 대해 옳고 Foo그것을 고쳤지만의 여러 인스턴스에 대한 당신의 요지는 보이지 않습니다 Foo. 올바르게 처리해야합니다.
terdon

@JohnDibling 업데이트를 참조하십시오.
terdon

1
100 줄의 "Foo"와 "Bar"가 포함 된 파일은보고하지 않습니다.
Stéphane Chazelas

0

테스트를 거치지 않았습니다.

find . -name "*.cpp" | xargs awk '/foo/{t=$0;c=10}/bar/{c=0;t=""}c{c--}t&&!c{print t;t=""}END&&t{print t}' 

그런 것.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.