grep이 10 줄 내에 표시되지 않는“Foo”인스턴스를 찾습니다. 트리를 검색하려고한다고

“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”가없는 간단한 검색을 구성하고 싶습니다.

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



답변

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.


답변

@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

답변

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

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}'

그런 것.